diff --git a/exam/.vscode/latex.code-snippets b/exam/.vscode/latex.code-snippets new file mode 100644 index 0000000..3196471 --- /dev/null +++ b/exam/.vscode/latex.code-snippets @@ -0,0 +1,277 @@ +{ + "Exc": { + "scope": "latex", + "prefix": ["\\exc"], + "body": + [ + "\\exc{}", + "\\begin{subexcs}", + " \\subexc{}", + " $0", + "\\end{subexcs}" + ], + "description": "Adds new exc with subexcs", + }, + + "Subexc": { + "scope": "latex", + "prefix": ["\\subexc"], + "body": + [ + "\\subexc{}", + "\\begin{ssubexcs}", + " \\ssubexc{}", + " $0", + "\\end{ssubexcs}" + ], + "description": "Adds new subexc with ssubexcs", + }, + + "Diagram": { + "scope": "latex", + "prefix": ["dia"], + "body": "\\includeDiagram[caption={}, width=1\\linewidth]{graphics/$0.tex}", + "description": "Include a diagram", + }, + + "Graph Table": { + "scope": "latex", + "prefix": ["graph-table"], + "body": + [ + "\\begin{figure}[H]", + " \\center", + " \\begin{tabular}{c|cc}", + " $A$ & \\multicolumn{2}{c}{$v$} \\\\", + " \\hline", + " & $a$ & $b$ \\\\", + " \\hline", + " $s_0$ & $s_$ & $s_$ \\\\", + " $s_1$ & $s_$ & $s_$ \\\\", + " $s_2$ & $s_$ & $s_$ \\\\", + " \\end{tabular}", + "\\end{figure}" + ], + "description": "Adds graph-table", + }, + + "Graph Table Double": { + "scope": "latex", + "prefix": ["graph-dtable"], + "body": + [ + "\\begin{figure}[H]", + " \\center", + " \\begin{tabular}{c|cc}", + " $A$ & \\multicolumn{2}{c}{$v$} \\\\", + " \\hline", + " & $a$ & $b$ \\\\", + " \\hline", + " $s_0$ & $s_$ & $s_$ \\\\", + " $s_1$ & $s_$ & $s_$ \\\\", + " $s_2$ & $s_$ & $s_$ \\\\", + " \\end{tabular}", + "\\end{figure}" + ], + "description": "Adds graph-table", + }, + + "Graph Table Line": { + "scope": "latex", + "prefix": ["gtl"], + "body": " $s_$ & $s_$ & $s_$ \\\\", + "description": "Adds line inside graph-table", + }, + + "Graph Table Double Line": { + "scope": "latex", + "prefix": ["gtdl"], + "body": " $s_$ & $s_$ & $s_$ & $s_$ & $s_$ \\\\", + "description": "Adds line inside a double graph-table", + }, + + "Induction Proof": { + "scope": "latex", + "prefix": ["prove-induction"], + "body": [ + "Base case:", + "", + "\\begin{align*}", + "", + "\\end{align*}", + "", + "Assume that", + "", + "\\[ $1 \\]", + "", + "Then", + "", + "\begin{align*}", + " &= $1 + \\\\", + " &=", + "\\end{align*}", + "", + "\\qed" + ], + "description": "Template for induction proof", + }, + + "Injective Proof": { + "scope": "latex", + "prefix": ["prove-injective"], + "body": [ + "In order for $f(x)$ to be injective, it has to hold that", + "", + "\\[ f(a) = f(b) \\Rightarrow a = b \\]", + "", + "\\begin{align*}", + " f(a) &= f(b) \\\\", + " $0", + "\\end{align*}", + "", + "Hence $f(x)$ is injective." + ], + "description": "Template for injective proof", + }, + + "Surjective Proof": { + "scope": "latex", + "prefix": ["prove-surjective"], + "body": [ + "In order for $f(x)$ to be surjective, it has to hold that", + "", + "\\[ \\forall x \\in SET \\exists y \\in SET [f(x) = y] \\]", + "", + "\\begin{align*}", + " y &= $1 \\\\", + "", + " x &= \\\\" , + "\\end{align*}", + "", + "$ $ makes up all the elements in SET", + "", + "\\begin{align*}", + " f(y) &= \\\\", + "", + "\\end{align*}", + "", + "Hence $f(x)$ is surjective" + ], + "description": "Template for surjective proof", + }, + + "Bijective Function Proof": { + "scope": "latex", + "prefix": ["prove-bijective"], + "body": [ + "\\textbf{Injective:}", + "", + "In order for $f(x)$ to be injective, it has to hold that", + "", + "\\[ f(a) = f(b) \\Rightarrow a = b \\]", + "", + "\\begin{align*}", + " f(a) &= f(b) \\\\", + " $0", + "\\end{align*}", + "", + "Hence $f(x)$ is injective.", + "", + "", + "\\textbf{Surjective:}", + "", + "In order for $f(x)$ to be surjective, it has to hold that", + "", + "\\[ \\forall x \\in SET \\exists y \\in SET [f(x) = y] \\]", + "", + "\\begin{align*}", + " y &= $1 \\\\", + "", + " x &= \\\\" , + "\\end{align*}", + "", + "$ $ makes up all the elements in SET", + "", + "\\begin{align*}", + " f(y) &= \\\\", + "", + "\\end{align*}", + "", + "Hence $f(x)$ is surjective", + "", + "", + "\\textbf{Inverse:}", + "", + "The inverse is the same as the expression which makes up $x$ which we used to prove that $f(x)$ is surjective. Hence", + "", + "\\[ f^{-1}(x) = \\]", + ], + "description": "Template for bijective proof", + }, + + "Equivalence Relation Proof": { + "scope": "latex", + "prefix": ["prove-eq-rel"], + "body": [ + "In order for this relation to be an equivalence equation, it has to be reflexive, symmetric and transitive.", + "", + "\\textbf{Reflexive:}", + "", + "\\[ \\]", + "", + "\\textbf{Symmetric:}", + "", + "\\[ \\]", + "", + "\\textbf{Transitive:}", + "", + "\\[ \\]", + "", + "Hence the relation is an equivalence relation", + ], + "description": "Template for equivalence relation proof", + }, + + "Partial Order Proof": { + "scope": "latex", + "prefix": ["prove-poset"], + "body": [ + "In order for this relation to be a partial order, it has to be reflexive, antisymmetric and transitive.", + "", + "\\textbf{Reflexive:}", + "", + "\\[ \\]", + "", + "\\textbf{Antisymmetric:}", + "", + "\\[ \\]", + "", + "\\textbf{Transitive:}", + "", + "\\[ \\]", + "", + "Hence the relation is a partial order", + ], + "description": "Template for poset proof", + }, + + "Poset MinMax": { + "scope": "latex", + "prefix": ["minmax-poset"], + "body": [ + "Minimal elements:", + " \\[ \\{ $0 \\} \\]", + "Maximal elements:", + " \\[ \\{ \\} \\]" + ], + "description": "Minimal maximal elements for poset", + }, + + "Binomial Coefficient": { + "scope": "latex", + "prefix": ["binom-co"], + "body": "\\[ \nCr{$0}{$1}x^{$2}y^{$1} = $3 x^{$2}y^{$1}\\]", + "description": "Formula for a binomial coefficient", + }, + +} \ No newline at end of file diff --git a/exam/.vscode/makefile.code-snippets b/exam/.vscode/makefile.code-snippets new file mode 100644 index 0000000..c715b89 --- /dev/null +++ b/exam/.vscode/makefile.code-snippets @@ -0,0 +1,16 @@ + +{ + "hasse": { + "scope": "makefile", + "prefix": ["hasse"], + "body": "python ../../python/Hasse.py graphics/src/$1.txt graphics/$1.tex", + "description": "Add hasse diagram", + }, + + "FSA": { + "scope": "makefile", + "prefix": ["fsa"], + "body": "python ../../python/FSA.py graphics/src/$1.txt graphics/$1.tex", + "description": "Add FSA diagram", + } +} \ No newline at end of file diff --git a/exam/Makefile b/exam/Makefile new file mode 100644 index 0000000..afa885c --- /dev/null +++ b/exam/Makefile @@ -0,0 +1,15 @@ +.DEFAULT_GOAL := default +.PHONY: default python + +default: python + pdflatex main.tex + +SRC_DIR := $(shell find graphics/src -type f -printf "%f\n" -name *.txt | cut -d '.' -f1) + +python: + @for f in $(SRC_DIR); \ + do \ + echo -e "\033[33mCOMPILING $$f.tex\033[0m"; \ + python ../exam_template/python/run.py graphics/src/$$f.txt graphics/$$f.tex; \ + echo ""; \ + done \ No newline at end of file diff --git a/exam/graphics/14.tex b/exam/graphics/14.tex new file mode 100644 index 0000000..3015926 --- /dev/null +++ b/exam/graphics/14.tex @@ -0,0 +1,24 @@ +\begin{tikzpicture} + \tikzset{ + ->, % makes the edges directed + >=Stealth, % makes the arrow heads bold + node distance=5cm, % specifies the minimum distance between two nodes. Change if necessary. + every state/.style={thick, fill=white}, % sets the properties for each ’state’ node + initial text=$ $, % sets the text that appears on the start arrow + } + +\node[state, ] (s0) {$s_0$}; +\node[state, right of=s0] (s1) {$s_1$}; +\node[state, right of=s1] (s2) {$s_2$}; + +\draw (s0) edge[bend right, below] node{$a,0$} (s1); +\draw (s0) edge[above] node{$c,1$} (s1); +\draw (s0) edge[loop,above] node{b,1} (s0); +\draw (s1) edge[above] node{$a,1$} (s2); +\draw (s1) edge[bend right, above] node{$b,0$} (s0); +\draw (s1) edge[bend right, above] node{$c,0$} (s0); +\draw (s2) edge[bend right, above] node{$a,0$} (s1); +\draw (s2) edge[bend left, below] node{$b,1$} (s0); +\draw (s2) edge[loop,above] node{c,0} (s2); + +\end{tikzpicture} \ No newline at end of file diff --git a/exam/graphics/14mod.tex b/exam/graphics/14mod.tex new file mode 100644 index 0000000..3fa76be --- /dev/null +++ b/exam/graphics/14mod.tex @@ -0,0 +1,24 @@ +\begin{tikzpicture} + \tikzset{ + ->, % makes the edges directed + >=Stealth, % makes the arrow heads bold + node distance=5cm, % specifies the minimum distance between two nodes. Change if necessary. + every state/.style={thick, fill=white}, % sets the properties for each ’state’ node + initial text=$ $, % sets the text that appears on the start arrow + } + +\node[state, ] (s0) {$s_0$}; +\node[state, right of=s0] (s1) {$s_1$}; +\node[state, right of=s1] (s2) {$s_2$}; + +\draw (s0) edge[bend right, above] node{$a,0$} (s1); +\draw (s0) edge[above] node{$c,1$} (s1); +\draw (s0) edge[loop,above] node{b,1} (s0); +\draw (s1) edge[above] node{$a,1$} (s2); +\draw (s1) edge[bend right=60, above] node{$b,0$} (s0); +\draw (s1) edge[bend right, above] node{$c,0$} (s0); +\draw (s2) edge[bend right, above] node{$a,0$} (s1); +\draw (s2) edge[bend left, below] node{$b,1$} (s0); +\draw (s2) edge[loop,above] node{c,0} (s2); + +\end{tikzpicture} \ No newline at end of file diff --git a/exam/graphics/16.tex b/exam/graphics/16.tex new file mode 100644 index 0000000..76d6da7 --- /dev/null +++ b/exam/graphics/16.tex @@ -0,0 +1,22 @@ +\newcommand{\arrow}[2]{\path [-{Latex[scale=1]}] (#1) edge (#2);} + +\begin{tikzpicture} + + \begin{scope}[every node/.style={shape=circle, fill=white, draw, inner sep=2pt}] + \node (A) at (0,2.5) {$A$}; +\node (B) at (-2.37764,0.77254) {$B$}; +\node (C) at (-1.46946,-2.02254) {$C$}; +\node (D) at (1.46946,-2.02254) {$D$}; +\node (E) at (2.37764,0.77254) {$E$}; + \end{scope} + + \begin{scope}[every draw/.style={}] + \draw (A) -- (B); +\draw (A) -- (C); +\draw (A) -- (E); +\draw (B) -- (D); +\draw (B) -- (E); +\draw (D) -- (E); + \end{scope} + +\end{tikzpicture} diff --git a/exam/graphics/17.tex b/exam/graphics/17.tex new file mode 100644 index 0000000..9f56778 --- /dev/null +++ b/exam/graphics/17.tex @@ -0,0 +1,37 @@ +\newcommand{\point}[4]{ + \node [label=#3:#4] (#1) at #2 {}; +} + +\newcommand{\wpath}[3]{ + \draw (#1) -- (#2) node [midway, above, sloped] (Tx#1#2) {{\tiny $#3$}}; +} + +\begin{tikzpicture}[] + \begin{scope}[every node/.style={fill=black, shape=circle, inner sep=1pt}] + \point{1}{(0,1)}{left}{$v_1$} + \point{2}{(1,2)}{above}{$v_2$} + \point{3}{(1,0)}{below}{$v_3$} + + \point{4}{(3,2)}{above left}{$v_4$} + \point{5}{(3,0)}{below right}{$v_5$} + + \point{6}{(5,2)}{above left}{$v_6$} + \point{7}{(5,0)}{above left}{$v_7$} + \point{8}{(6,1)}{above right}{$v_8$} + \end{scope} + + \wpath{1}{3}{1} + \wpath{2}{3}{2} + \wpath{7}{8}{2} + \wpath{3}{5}{3} + \wpath{4}{5}{4} + \wpath{6}{8}{5} + \wpath{5}{6}{6} + \wpath{6}{7}{7} + \wpath{5}{7}{8} + \wpath{4}{6}{9} + \wpath{2}{5}{10} + \wpath{2}{4}{11} + \wpath{1}{2}{12} + +\end{tikzpicture} diff --git a/exam/graphics/17_2.tex b/exam/graphics/17_2.tex new file mode 100644 index 0000000..06e6527 --- /dev/null +++ b/exam/graphics/17_2.tex @@ -0,0 +1,31 @@ +\newcommand{\point}[4]{ + \node [label=#3:#4] (#1) at #2 {}; +} + +\newcommand{\wpath}[3]{ + \draw (#1) -- (#2) node [midway, above, sloped] (Tx#1#2) {{\tiny $#3$}}; +} + +\begin{tikzpicture}[] + \begin{scope}[every node/.style={fill=black, shape=circle, inner sep=1pt}] + \point{1}{(0,1)}{left}{$v_1$} + \point{2}{(1,2)}{above}{$v_2$} + \point{3}{(1,0)}{below}{$v_3$} + + \point{4}{(3,2)}{above left}{$v_4$} + \point{5}{(3,0)}{below right}{$v_5$} + + \point{6}{(5,2)}{above left}{$v_6$} + \point{7}{(5,0)}{above left}{$v_7$} + \point{8}{(6,1)}{above right}{$v_8$} + \end{scope} + + \wpath{1}{3}{1} + \wpath{2}{3}{2} + \wpath{7}{8}{2} + \wpath{3}{5}{3} + \wpath{4}{5}{4} + \wpath{6}{8}{5} + \wpath{5}{6}{6} + +\end{tikzpicture} diff --git a/exam/graphics/3.tex b/exam/graphics/3.tex new file mode 100644 index 0000000..3484327 --- /dev/null +++ b/exam/graphics/3.tex @@ -0,0 +1,10 @@ + + \begin{truthtable} + {c|c|e|c|e} + {$p$ & $q$ & $ \neg p$ & $p \wedge q$ & $ \neg (p \wedge q)$} + \T & \T & \F & \T & \F \\ + \T & \F & \F & \F & \T \\ + \F & \T & \T & \F & \T \\ + \F & \F & \T & \F & \T \\ + \end{truthtable} + \ No newline at end of file diff --git a/exam/graphics/5.tex b/exam/graphics/5.tex new file mode 100644 index 0000000..529f423 --- /dev/null +++ b/exam/graphics/5.tex @@ -0,0 +1,27 @@ + +In order for this relation to be an equivalence equation, it has to be reflexive, symmetric and transitive. + +\textbf{Reflexive:} + +All elements are related to themself + +\[ DD, AA, CC, BB \] + +\textbf{Symmetric:} + +All relations has its symmetric counterpart + +\begin{gather*} +CD\text{ and }DC +\end{gather*} + +\textbf{Transitive:} + +All pair of relations where $xRy$ and $yRz$ has its transitive counterpart + +\begin{gather*} +DC\text{ and }CD\text{ with }DD \\ +CD\text{ and }DC\text{ with }CC +\end{gather*} + +Hence the relation is an equivalence relation \ No newline at end of file diff --git a/exam/graphics/6.tex b/exam/graphics/6.tex new file mode 100644 index 0000000..ad15674 --- /dev/null +++ b/exam/graphics/6.tex @@ -0,0 +1,15 @@ +\begin{tikzpicture} + \tikzset{every node/.style={shape=circle,draw,fill=white,inner sep=2pt}} + +\node (3) at (0, 0) {$3$}; +\node (4) at (1, 0) {$4$}; +\node (6) at (0, 1) {$6$}; +\node (12) at (0, 2) {$12$}; +\node (20) at (1, 1) {$20$}; + +\draw (3) -- (6); +\draw (4) -- (12); +\draw (4) -- (20); +\draw (6) -- (12); + +\end{tikzpicture} diff --git a/exam/graphics/src/14.txt b/exam/graphics/src/14.txt new file mode 100644 index 0000000..9ceecbf --- /dev/null +++ b/exam/graphics/src/14.txt @@ -0,0 +1,14 @@ +# FSA +0 +1 r0 +2 r1 + +0 a,0 )d 1 +0 c,1 u 1 +o b,1 u 0 +1 a,1 u 2 +1 b,0 )u 0 +1 c,0 )u 0 +2 a,0 )u 1 +2 b,1 (d 0 +o c,0 u 2 \ No newline at end of file diff --git a/exam/graphics/src/16.txt b/exam/graphics/src/16.txt new file mode 100644 index 0000000..80a806e --- /dev/null +++ b/exam/graphics/src/16.txt @@ -0,0 +1,8 @@ +# Graph +matrix + +01101 +10011 +10000 +01001 +11010 \ No newline at end of file diff --git a/exam/graphics/src/3.txt b/exam/graphics/src/3.txt new file mode 100644 index 0000000..d80395a --- /dev/null +++ b/exam/graphics/src/3.txt @@ -0,0 +1,2 @@ +# Truthtable +p, q, E not p, p and q, E not (p and q) \ No newline at end of file diff --git a/exam/graphics/src/5.txt b/exam/graphics/src/5.txt new file mode 100644 index 0000000..e0eb251 --- /dev/null +++ b/exam/graphics/src/5.txt @@ -0,0 +1,4 @@ +# Relations +proveEquivalence + +AA BB CC CD DD DC \ No newline at end of file diff --git a/exam/graphics/src/genDivPoset.py b/exam/graphics/src/genDivPoset.py new file mode 100644 index 0000000..0dc4be2 --- /dev/null +++ b/exam/graphics/src/genDivPoset.py @@ -0,0 +1,7 @@ +def genDivPosetFrom(s): + return set((s1,s2) for s1 in s for s2 in s if s2 % s1 == 0 and s1 != s2) + +s = set([3,4,6,12,20]) +poset = genDivPosetFrom(s) + +print('\n'.join(str(x) for x in poset)) diff --git a/exam/main.tex b/exam/main.tex new file mode 100644 index 0000000..87e8861 --- /dev/null +++ b/exam/main.tex @@ -0,0 +1,348 @@ +\documentclass[12pt]{article} +\usepackage{ntnu} +\usepackage{ntnu-math} +\usepackage{ntnu-code} + +\author{10112} +\title{MA0301 Spring 2021} + +\usetikzlibrary{automata, positioning, arrows.meta} + +\newcommand{\I}{\Huge\textbf{I}} +\newcommand{\II}{\Huge\textbf{I\!I}} +\newcommand{\III}{\Huge\textbf{I\!I\!I}} + +\usepackage{listings} +\usepackage{blkarray} + +\renewcommand{\theenumi}{\arabic{enumi}} +\renewcommand{\theenumii}{(\arabic{enumii})} +\renewcommand{\theenumiii}{\alph{enumiii})} + + + \newcommand{\checkEdge}[3]{ + Find next edge with the minimum weight. \\ + The edge between #1 and #2 has weight #3. \\ + Does adding it connect two separate subgraphs? \\ + } + \newcommand{\yes}{\textbf{Yes. Add edge} \\[2ex]} + \newcommand{\no}{\textbf{No. Discard edge} \\[2ex]} + +\begin{document} + + %%%%%%%% 2 + + \begin{align*} + \neg (\exists x \forall y (\neg P(x,y) \wedge Q(x,y))) \\ + \forall x \exists y \neg(\neg P(x,y) \wedge Q(x,y)) \\ + \forall x \exists y (\neg \neg P(x,y) \vee \neg Q(x,y)) \\ + \forall x \exists y (P(x,y) \vee \neg Q(x,y)) \\ + \end{align*} + + %%%%%%%% 3 + \break + + 1. + + \input{graphics/3.tex} + + 2. + + By looking at the truthtable, we can see that $\neg p \not\equiv \neg(p \wedge q)$ \\ + + 3. + + \begin{gather*} + (p \downarrow q) \downarrow (p \downarrow q) \\ + \neg ( \neg (p \wedge q) \wedge \neg (p \wedge q)) \\ + \neg ( (\neg p \vee \neg q) \wedge (\neg p \vee \neg q)) \\ + \neg (\neg p \vee \neg q) \vee \neg (\neg p \vee \neg q)) \\ + \neg \neg p \vee \neg \neg q \vee \neg \neg p \vee \neg \neg q \\ + p \vee q \vee p \vee q \\ + p \vee q \\ + \end{gather*} + + \[ (p \downarrow q) \downarrow (p \downarrow q) \equiv p \vee q \] + + + %%%%%%%% 5 + \break + + \[ R := \{ (0,0), (1,1), (2,2), (2,3) \} \] + + $R$ is not an equivalence relation. \\ + + For reflexivity, it's missing one relation + + \[ (3,3) \] + + For symmetry, it's missing one relation + + \[ (3,2) \] + + For transitivity, it will by now have all missing elements \\ + + \[ (2,3) \text{ and } (3,2) \rightarrow (2,2) \] + \[ (3,2) \text{ and } (2,3) \rightarrow (3,3) \] + + \[ R := \{ (0,0),(1,1),(2,2),(2,3), (3,2), (3,3) \} \] + + %%%%%%%% 6 + \break + + \[ A := \{3, 4, 6, 12, 20 \} \] + + \includeDiagram[scale=2, width=5cm]{graphics/6.tex} + + \center + By looking at the hasse diagram, we can see that + + \[ \text{Minimal elements: } \{ 3, 4 \} \] + \[ \text{Maximal elements: } \{ 12, 20 \} \] + + %%%%%%%% 7 + \break + + Base case: + + \begin{align*} + \sum^3_{i=3} 3^i = \frac{3(3^3-9)}{2} \\[2ex] + 3^3 = \frac{3(27-9)}{2} \\[2ex] + 27 = \frac{3(18)}{2} \\[2ex] + 27 = 9 \cdot 3 \\[2ex] + 27 = 27 \\[2ex] + \end{align*} + + Assume that + + \[ \sum^n_{i=3} 3^i = \frac{3(3^n-9)}{2} \qquad \text{for } n > 2 \] + + Then + + \begin{align*} + \sum^{n+1}_{i=3} 3^i &= 3^3 + 3^4 + \ldots + 3^n + 3^{n+1} \\[2ex] + &= \frac{3(3^n-9)}{2} + 3^{n+1} \\[2ex] + &= \frac{3^{n+1} - 3 \cdot 9 + 2 \cdot 3^{n+1}}{2} \\[2ex] + &= \frac{3 \cdot 3^{n+1} - 3 \cdot 9}{2} \\[2ex] + &= \frac{3(3^{n+1} - 9)}{2} \\[2ex] + \end{align*} + + \qed + + %%%%%%%% 8 + \break + + \[ + \{ a_n \}_{n>0} = + \begin{cases} + a_1 = 3 \\ + a_2 = 6 \\ + a_n = a_n + a_{n-1} \quad \text{for } n > 2 \\ + \end{cases} + \] + + \textbf{Base case:} + + For $a_3$, we have that + + \begin{align*} + a_3 &= a_2 + a_1 \\ + &= 6 + 3 \\ + &= 3(3) \\ + &\Rightarrow a_3 \bmod 3 = 0 + \end{align*} + + For $a_4$, we have that + + \begin{align*} + a_4 &= a_3 + a_2 \\ + &= 9 + 6 \\ + &= 3(5) \\ + &\Rightarrow a_4 \bmod 3 = 0 + \end{align*} + + \textbf{Inductive step:} + + Assume that for $k_1 \in \N$, $k_2 \in \N$: + + \begin{align*} + a_n &= 3(k_1) \\ + a_{n-1} &= 3(k_2) \\ + \end{align*} + + Then + + \begin{align*} + a_{n+1} &= a_n + a_{n-1} \\ + &= 3(k_1) + 3(k_2) \\ + &= 3(k_1 + k_2) \\ + &\Rightarrow a_{n+1} \bmod 3 = 0 + \end{align*} + + \qed + + + %%%%%%%% 10 + \break + + \[ f : \Z \rightarrow \Z \] + \[ f(x) = x-7 \] + + \textbf{Injective:} + + In order for $f(x)$ to be injective, it has to hold that + + \[ f(a) = f(b) \Rightarrow a = b \] + + \begin{align*} + f(a) &= f(b) \\ + a - 7 &= b - 7 \\ + a &= b \\ + \end{align*} + + Hence $f(x)$ is injective. \\ + + + \textbf{Surjective:} + + In order for $f(x)$ to be surjective, it has to hold that + + \[ \forall x \in \Z \exists y \in \Z [f(x) = y] \] + + \begin{align*} + y &= x - 7 \\ + x &= y + 7 \\ + \end{align*} + + $ x = y + 7 $ makes up all the elements in $\Z$ + + \begin{align*} + f(x) &= x - 7 \\ + &= (y + 7) - 7 \\ + &= y + \end{align*} + + Hence $f(x)$ is surjective \\ + + \textbf{Conclusion:} + + Since $f(x)$ is both injective and surjective, it is by definition bijective + + + %%%%%%%% 11 + \break + + There are no $x^6y^6$ in the expansion of $(3x^5 + 2y)^6$ \\ + + For $x^{10}y^4$, there is + + \[ \binom{6}{4}(3x^5)^{6-4}(2y)^4 = 2160 x^{10}y^4 \] + + %%%%%%%% 12 + \break + + The number of permutations of the letters "ALLTALK" is + + \[ \frac{7!}{(7-7)!2!3!} = 420 \] + + If all of the Ls has to be together, we can think of this block like one letter. \\ + + That leaves us with 5 letters, where one of them is repeated once. \\ + + \[ \frac{5!}{(5-5)!2!} = 60 \] + + %%%%%%%% 14 + \break + + \includeDiagram{graphics/14mod.tex} + + Assuming the automation is starting at $s_0$, the output for $acabacab$ would be + + \begin{gather*} + s_0 \xrightarrow{a,0} s_1 \\ + s_1 \xrightarrow{c,0} s_0 \\ + s_0 \xrightarrow{a,0} s_1 \\ + s_1 \xrightarrow{b,0} s_0 \\ + s_0 \xrightarrow{a,0} s_1 \\ + s_1 \xrightarrow{c,0} s_0 \\ + s_0 \xrightarrow{a,0} s_1 \\ + s_1 \xrightarrow{b,0} s_0 \\ + \end{gather*} + + \[ 00000000 \] + + %%%%%%%% 15 + \break + + $L$ is a language that only contains words which fits the regular expresson $r$, where + + \[ r = b^*ab^*ab^* \] + + or described with words, \\ + + \center "$L$ is a language that only has words that contain exactly two of the letter $a$" + + %%%%%%%% 16 + \break + + \[ + \begin{blockarray}{cccccc} + & A & B & C & D & E \\ + \begin{block}{c[ccccc]} + A & 0 & 1 & 1 & 0 & 1 \\ + B & 1 & 0 & 0 & 1 & 1 \\ + C & 1 & 0 & 0 & 0 & 0 \\ + D & 0 & 1 & 0 & 0 & 1 \\ + E & 1 & 1 & 0 & 1 & 0 \\ + \end{block} + \end{blockarray} + \] + + \includeDiagram{graphics/16.tex} + + + %%%%%%%% 17 + \break + + \includeDiagram[scale=1.6]{graphics/17.tex} + + Performed Kruskal's algorithm as follows: \\[2ex] + + \textbf{Remove all edges} \\ + + \checkEdge{1}{3}{1} + \yes + \checkEdge{2}{3}{2} + \yes + \checkEdge{7}{8}{2} + \yes + \checkEdge{3}{5}{3} + \yes + \checkEdge{4}{5}{4} + \yes + \checkEdge{6}{8}{5} + \yes + \checkEdge{5}{6}{6} + \yes + By now, the graph is already spanning so there aren't any more disconnected subgraphs to connect, but I'll check the last ones anyway, like for-looping over the set of edges in a computer program would. \\ + + \checkEdge{6}{7}{7} + \no + \checkEdge{5}{7}{8} + \no + \checkEdge{4}{6}{9} + \no + \checkEdge{2}{5}{10} + \no + \checkEdge{2}{4}{11} + \no + \checkEdge{1}{2}{12} + \no + + \break{} + + \textbf{Result: } + + \includeDiagram[scale=1.6]{graphics/17_2.tex} + +\end{document} diff --git a/exam/python/FSA.py b/exam/python/FSA.py new file mode 100644 index 0000000..66967e8 --- /dev/null +++ b/exam/python/FSA.py @@ -0,0 +1,80 @@ +from sys import argv +from pathlib import Path +import re + +from common import replaceContent + +placementChart = { + 'a': 'above', + 'b': 'below', + 'l': 'left', + 'r': 'right' +} + +def generateEdge(inputLine): + s_src, msg, opts, s_dest = inputLine.split(' ') + + out_options = [] + + if '(' in opts or ')' in opts: + out_options.append('bend left' if '(' in opts else 'bend right') + + if any(x in opts for x in 'udlr'): + out_options.append( + 'above' if 'u' in opts else \ + 'below' if 'd' in opts else \ + 'left' if 'l' in opts else \ + 'right' if 'r' in opts else '' + ) + + + if s_src == 'o': + return f'\draw (s{s_dest}) edge[loop,{",".join(out_options)}] node{{{msg}}} (s{s_dest});' + else: + return f'\draw (s{s_src}) edge[{", ".join(out_options)}] node{{${msg}$}} (s{s_dest});' + +def generateNode(inputLine): + s_src, opts = inputLine.split(' ') + + out_opts = [] + if 's' in opts: + out_opts.append('initial') + if 'f' in opts: + out_opts.append('accepting') + + if place := re.search('[udlr]\d+', opts): + out_opts.append(placementChart[place.group()[0]] + ' of=s' + place.group()[1]) + + return f'\\node[state, {", ".join(out_opts)}] (s{s_src}) {{$s_{s_src}$}};' + + +def generate_latex(inputLines): + + nodes, edges = inputLines.split('\n\n') + + output=[] + + for node in nodes.split('\n'): + output.append(generateNode(node)) + + output.append('') + + for edge in edges.split('\n'): + output.append(generateEdge(edge)) + + return '\n'.join(output) + +def processFileContent(raw): + content = generate_latex(raw) + return replaceContent(content, 'FSA') + +if __name__ == '__main__': + pass + # filename = argv[1] + + # with open(filename) as file: + # content = generate_latex(file.read()) + + # with open(str(Path(__file__).parent.absolute()) + '/tex_templates/FSA.tex') as template: + # with open(argv[2], 'w') as destination_file: + # destination_file.write(template.read().replace('%CONTENT', content)) \ No newline at end of file diff --git a/exam/python/Graph.py b/exam/python/Graph.py new file mode 100644 index 0000000..341c70a --- /dev/null +++ b/exam/python/Graph.py @@ -0,0 +1,261 @@ +from sys import argv +from pathlib import Path +from math import sin, cos, pi + +from common import printc, printerr, replaceContent + +class Matrix: + """ Adjacency matrix which supports 0 and 1 """ + + def __init__(self, matrix): + self.matrix = matrix + + def __iter__(self): + return iter(self.matrix) + + def __len__(self): + return len(self.matrix) + + def __eq__(self, other): + return self.matrix == other + + def __str__(self): + return "\n".join(' '.join('\033[32m1\033[0m' if b else '\033[31m0\033[0m' for b in line) for line in self) + + def __getitem__(self, key): + return self.matrix[key] + + def __setitem__(self, key, item): + self.matrix[key] = item + + @classmethod + def fromGraph(cls, graph): + return graph.toMatrix() + + @classmethod + def fromString(cls, string): + matrix = [] + for line in string.split('\n'): + matrix.append([True if char == '1' else False for char in line]) + + if len(matrix) != len(matrix[0]): + raise ValueError('Matrix not covering all points') + + return cls(matrix) + + def toGraph(self): + nodes = [chr(i + 65) for i in range(len(self))] + + edges = [] + for i, line in enumerate(self): + edges += [(nodes[i],nodes[j]) for j, b in enumerate(line) if b] + + return Graph(nodes, edges) + + def toLaTeX(self): + return '\\begin{pmatrix}\n' \ + + '\n'.join(' ' + ' & '.join('1' if b else '0' for b in line) + ' \\\\' for line in self.matrix) \ + + '\n\\end{pmatrix}' + + +class Graph: + def __init__(self, nodes, edges): # Nodes = str, Edges = (str,str) + self.nodes = nodes + self.edges = edges + + def __str__(self): + printc('Is undirected: ' + str(self.isUndirected())) + return \ + f'Nodes: {" ".join(self.nodes)}\n' \ + + f'Edges: {" ".join(x+y for x,y in (self.undirectedEdgeSet() if self.isUndirected() else self.edges))}' + + @classmethod + def fromMatrix(cls, matrix): + return matrix.toGraph() + + @classmethod + def fromString(cls, string): + splitData = string.split('\n\n') + + if splitData[0] == 'complete': + data1 = 'undirected' + nodes = [chr(i + 65) for i in range(int(splitData[1]))] + data2 = ' '.join(nodes) + data3 = '\n'.join([a+b for a in nodes for b in nodes if a != b]) + elif splitData[0] == 'matrix': + return Graph.fromMatrix(Matrix.fromString(splitData[1])) + else: + data1, data2, data3 = splitData + + graphType = data1 + nodes = data2.split(' ') + edges = [(x,y) for x,y in data3.split('\n')] + + if graphType == 'undirected': + edges = [(a,b) for a in nodes for b in nodes if (a,b) in edges or (b,a) in edges] + + return cls(nodes, edges) + + def toMatrix(self): + rows = [] + for node in self.nodes: + rows.append([(node,node2) in self.edges for node2 in self.nodes]) + return Matrix(rows) + + def isUndirected(self): + matrix = self.toMatrix() + + flipv = lambda matrix: list(reversed(matrix)) + rot90cc = lambda matrix: list(list(x) for x in zip(*reversed(matrix))) + + return matrix == rot90cc(flipv(matrix)) \ + and all(matrix[i][i] == 0 for i in range(len(matrix))) + + def undirectedEdgeSet(self): + edges = self.edges + if self.isUndirected(): + edges = sorted(list(set((x,y) if x < y else (y,x) for x,y in edges))) + return edges + + # def latexifyEulerPath(self): + # degrees = [sum(line) for line in self.toMatrix()] + + # def latexifyEulerCircuit(): + # pass + + def getKruskalsSpanningTree(self, weigths): # weights = dict[str, int] + edges = [] + connected_subgraphs = [set(v) for v in self.nodes] + + if not all(n in ''.join(x+y for x,y in self.edges) for n in self.nodes): + printerr('Not all nodes are connected. There is no spanning tree to be made') + return + + def find_subgraph_containing(v): + return [x for x in connected_subgraphs if v in x][0] + + def merge_subgraphs_that_contains(u, v): + subgraph_v = find_subgraph_containing(v) + new_connected_subgraphs = [x for x in connected_subgraphs if v not in x] + new_connected_subgraphs = [subgraph_v.union(x) if u in x else x for x in new_connected_subgraphs] + return new_connected_subgraphs + + sorted_edges = [ e for e in sorted(self.edges, key=lambda e: weigths[e[0] + e[1]]) ] + + for u,v in sorted_edges: + if find_subgraph_containing(u) != find_subgraph_containing(v): + edges += [(u, v)] + connected_subgraphs = merge_subgraphs_that_contains(u, v) + + edges += [(y,x) for x,y in edges] + + return Graph(self.nodes, edges) + + def toLaTeX(self): + zippedNodes = zip(self.nodes, generateNodeCoords(len(self.nodes))) + nodeString = '\n'.join(f'\\node ({name}) at ({x},{y}) {{${name}$}};' for name,(x,y) in zippedNodes) + + if self.isUndirected(): + edgeString = '\n'.join(f'\\draw ({x}) -- ({y});' for x,y in self.undirectedEdgeSet()) + else: + edgeString = '\n'.join( + f'\\draw [-{{Latex[scale=1]}}, bend left=8] ({x}) to ({y});' if y != x and (y,x) in self.edges + else f'\\draw [-{{Latex[scale=1]}}] ({x}) to [{generateLoopInOutAngle(x, self.nodes)},looseness=8] ({y});' if x == y + else f'\\draw [-{{Latex[scale=1]}}] ({x}) to ({y});' + for x,y in self.edges + ) + + return (nodeString, edgeString) + + def toLaTeXWithWeights(self, weights): + zippedNodes = zip(self.nodes, generateNodeCoords(len(self.nodes))) + nodeString = '\n'.join(f'\\node ({name}) at ({x},{y}) {{${name}$}};' for name,(x,y) in zippedNodes) + + if self.isUndirected(): + edgeString = '\n'.join(f'\\draw ({x}) -- ({y}) node [midway, above, sloped] (Tx{x+y}) {{{weights[x+y]}}};' for x,y in self.undirectedEdgeSet()) + + return (nodeString, edgeString) + + +def generateLoopInOutAngle(node, nodes): + baseAngle = 360 / len(nodes) + nodeNum = [i for i,n in enumerate(nodes) if n == node][0] + angle = nodeNum * baseAngle + 90 + return f'out={angle + 15},in={angle - 15}' + + +def generateNodeCoords(n): + vectorLength = n / 2 + degreeToTurn = (2 * pi) / n + + + nodeCoords = [(0, vectorLength)] + for node in range(n): + prev_x = nodeCoords[-1][0] + prev_y = nodeCoords[-1][1] + + nodeCoords.append(( + round(cos(degreeToTurn) * prev_x - sin(degreeToTurn) * prev_y, 5), + round(sin(degreeToTurn) * prev_x + cos(degreeToTurn) * prev_y, 5) + )) + + return nodeCoords + +def extractWeights(string): + weights = dict() + lines = string.split('\n') + for i in range(4, len(lines)): + edge, weight = lines[i].split(' ') + weights[edge] = int(weight) + weights[edge[1] + edge[0]] = int(weight) + lines[i] = edge + return ('\n'.join(lines), weights) + +def processFileContent(raw): + lines = raw.split('\n') + lines.pop(1) + outputType = lines.pop(0) + + if lines[0] == 'kruskals': + lines[0] = 'undirected' + string, weights = extractWeights('\n'.join(lines)) + graph1 = Graph.fromString(string) + graph2 = graph1.getKruskalsSpanningTree(weights) + return replaceContent( + (graph1.toLaTeXWithWeights(weights), graph2.toLaTeXWithWeights(weights)), + 'Kruskals', + replaceF = lambda temp, cont: + temp.replace('%NODES1', cont[0][0]) + .replace('%EDGES1', cont[0][1]) + .replace('%NODES2', cont[1][0]) + .replace('%EDGES2', cont[1][1]) + ) + + + graph = Graph.fromString('\n'.join(lines)) + + print(graph) + print(graph.toMatrix()) + + if outputType == 'toGraph': + content = graph.toLaTeX() + return replaceContent( + content, + 'Graph', + replaceF = lambda temp, cont: temp.replace('%NODES', cont[0]).replace('%EDGES', cont[1]) + ) + else: + content = graph.toMatrix().toLaTeX() + return replaceContent(content, 'Matrix') + +if __name__ == '__main__': + g = Graph.fromString('complete\n\n6') + print(g) + + import random + + d = dict() + for e in g.edges: + d[str(e)] = random.randint(0, 10) + + print(g.getKruskalsSpanningTree(d)) \ No newline at end of file diff --git a/exam/python/InclusionExclusion.py b/exam/python/InclusionExclusion.py new file mode 100644 index 0000000..b8ee340 --- /dev/null +++ b/exam/python/InclusionExclusion.py @@ -0,0 +1,46 @@ +from itertools import combinations + +def latexifyCondition(condition): + return condition[0] if condition[1] else f'\\overline{{{condition[0]}}}' + +def latexify(listOfConditions): # [(str,bool)] + lines = [] + for n in range(1, len(listOfConditions) + 1): + line = [] + for x in combinations(listOfConditions, n): + line.append(''.join(latexifyCondition(c) for c in x)) + lines.append(line) + + linestrs = [] + for line in lines: + linestr = ' + '.join(f'N({x})' for x in line) + if len(line) > 1: + linestr = f'\\left[ {linestr} \\right]' + linestrs.append(linestr) + + b = False + result = '\\begin{align*}\nN' + for l in linestrs: + result += ' &+ ' if b else ' &- ' + result += l + ' \\\\\n' + b = not b + result += '\\end{align*}' + return result + +def parseInput(string): + result = [] + for x in string.split(' '): + if x.startswith('!'): + result.append((x[1:], False)) + else: + result.append((x, True)) + return result + +def processFileContent(raw): + listOfConditions = parseInput(raw) + content = latexify(listOfConditions) + return content + +if __name__ == '__main__': + pass + \ No newline at end of file diff --git a/exam/python/Powerset.py b/exam/python/Powerset.py new file mode 100644 index 0000000..486a7ff --- /dev/null +++ b/exam/python/Powerset.py @@ -0,0 +1,64 @@ +from itertools import combinations + +from common import replaceContent + +class Set: + def __init__(self, elements): + self.elements = set(elements) + + def __str__(self): + if len(self) == 0: return "\\emptyset" + return f"\\{{ {', '.join(sorted(self.elements))} \\}}" + + def __iter__(self): + return list(sorted(self.elements)) + + def __len__(self): + return len(self.elements) + + def __gt__(self, value): + if len(self) != len(value): + return len(self) > len(value) + return self.elements > value.elements + + def __ge__(self, value): + return self > value + + def __lt__(self, value): + return not self > value + + def __le__(self, value): + return self < value + + @classmethod + def fromString(cls, string): + return cls(string.split(' ')) + + def cardinality(self): + return len(self) + + def powerset(self): + powerset = [] + for i in range(len(self) + 1): + for subset in combinations(self.elements, i): + powerset.append(Set(list(subset))) + return Set(powerset) + + def to_vertical_latex(self): + column = [] + for e in sorted(self.elements): + column.append(str(e) + ' \\\\') + return '\\{\n' + '\n'.join(column) + '\n\\}' + + +def processFileContent(raw): + s = Set.fromString(raw) + content = s.powerset().to_vertical_latex() + + return replaceContent(content, 'Powerset') + +#TODO: make process input func + +if __name__ == "__main__": + print(Set(['a', 'b', 'c']).powerset().to_vertical_latex()) + # print(a for a in Set(['A', 'B', 'C']).powerset()) \ No newline at end of file diff --git a/exam/python/Python.py b/exam/python/Python.py new file mode 100644 index 0000000..8178507 --- /dev/null +++ b/exam/python/Python.py @@ -0,0 +1,26 @@ +import tempfile +import subprocess +import os +import sys + +from common import replaceContent + +def makeTmpFile(content): + fd, path = tempfile.mkstemp() + while ('_' in path): + fd, path = tempfile.mkstemp() + with os.fdopen(fd, 'w') as tmp: + tmp.write(content) + return path + +def grabOutput(path): + return subprocess.check_output(['python', path]).decode(sys.stdout.encoding) + +def processFileContent(content): + path = makeTmpFile(content) + output = grabOutput(path) + return replaceContent( + (path, output), + 'Python', + lambda temp, cont: temp.replace('%CODEFILE', cont[0]).replace('%OUTPUT', cont[1]) + ) diff --git a/exam/python/Relations.py b/exam/python/Relations.py new file mode 100644 index 0000000..40db915 --- /dev/null +++ b/exam/python/Relations.py @@ -0,0 +1,298 @@ +from sys import argv +from pathlib import Path + +from common import printc, printerr, replaceContent +import Graph + +# Increase if hasse diagram becomes too clobbered + +def pairToString(pair): + if str(pair[0]).isdigit() or str(pair[1]).isdigit(): + return f'({pair[0]},{pair[1]})' + else: + return pair[0] + pair[1] + +def stringToPair(string): + if string[0] == '(': + return tuple([string.split(',')[0][1:], string.split(',')[0][:-1]]) + else: + return tuple(list(string)) + +def textLatex(string): + return '\\text{' + string + '}' + +def mathModeListLatex(elements): + if len(elements) > 7 or len(elements) and max(len(str(e)) for e in elements) > 10: + return '\\begin{gather*}\n' \ + + ' \\\\\n'.join(elements) + '\n' \ + + '\\end{gather*}' + else: + return f'\\[ {", ".join(elements)} \\]' + + + +class Relation: + def __init__(self, pairs): # pair = (str,str) + self.pairs = set(pairs) + + def __str__(self): + return f'\\{{ {", ".join(x + y for x,y in self.pairs)} \}}' + + @classmethod + def fromString(cls, string): + relations = (stringToPair(x) for x in string.split(' ')) + return cls(relations) + + @classmethod + def fromDivisibilityPosetTo(cls, n): + pairs = set() + for dst in range(n): + pairs.add((dst, dst)) + for src in range(1, dst): + if dst % src == 0: + pairs.add((src, dst)) + return cls(pairs) + + + def isReflexive(self, verbose=False): + elements = set(list(sum(self.pairs, ()))) + result = all((x,x) in self.pairs for x in elements) + + if not result: + printc('Not reflexive, missing following pairs:', color='green') + missingPairs = [(x,x) for x in elements if (x,x) not in self.pairs] + print(f'\\[ {", ".join(pairToString(p) for p in missingPairs)} \\]') + if verbose: + return (False, missingPairs) + + if verbose: + return (True, [(x,x) for x in elements if (x,x) in self.pairs]) + + return result + + def isSymmetric(self, verbose=False): + result = all((y,x) in self.pairs for x,y in self.pairs) + + if not result: + printc('Not symmetric, missing following pairs:', color='green') + missingPairs = [(x,y) for x,y in self.pairs if (y,x) not in self.pairs] + print(f'\\[ {", ".join(pairToString(p) for p in missingPairs)} \\]') + if verbose: + return (False, missingPairs) + + if verbose: + return (True, [((x,y), (y,x)) for x,y in self.pairs if (y,x) in self.pairs and x < y]) + + return result + + def isAntiSymmetric(self, verbose=False): + result = not any((y,x) in self.pairs and y != x for x,y in self.pairs) + + if not result: + printc('Not antisymmetric, following pairs are symmetric:', color='green') + symmetricPairs = [((x,y), (y,x)) for x,y in self.pairs if (y,x) in self.pairs and y > x] + print(f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in symmetricPairs)} \\]') + if verbose: + return (False, symmetricPairs) + + if verbose: + return (True, []) + + return result + + def isTransitive(self, verbose=False): + result = True + transitivePairs = [] + nonTransitivePairs = [] + + for x,y in self.pairs: + for z,w in self.pairs: + if not (y != z or ((x,w) in self.pairs)): + nonTransitivePairs.append(((x,y), (z,w))) + result = False + elif (y == z and x != y != w and ((x,w) in self.pairs)): + transitivePairs.append(((x,y), (z,w))) + + if not result: + printc('Not transitive, following pairs are missing its transitive counterpart:', color='green') + print(f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" without ")}{pairToString((p[0], q[1]))}""" for p,q in nonTransitivePairs)} \\]') + if verbose: + return (False, nonTransitivePairs) + + if verbose: + return (True, transitivePairs) + + return result + + def isEquivalenceRelation(self, verbose=False): + + if verbose: + isReflexive, reflexivePairs = self.isReflexive(verbose=True) + isSymmetric, symmetricPairs = self.isSymmetric(verbose=True) + isTransitive, transitivePairs = self.isTransitive(verbose=True) + + if not isReflexive: + return 'The relation is not an equivalence relation, because it is not reflexive.' + + ' The following elements should be related:\n\n' + + f'\\[ {", ".join(pairToString(p) for p in reflexivePairs)} \\]' + if not isSymmetric: + return 'The relation is not an equivalence relation, because it is not symmetric.' + + ' It is missing the following symmetric pairs of relations\n\n' + + f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in symmetricPairs)} \\]' + if not isTransitive: + return 'The relation is not an equivalence relation, because it is not transitive.' + + ' The following pairs of relations are missing its transitive counterpart\n\n' + + f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" without ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs)} \\]' + + + rxStr = mathModeListLatex([pairToString(p) for p in reflexivePairs]) + smStr = mathModeListLatex([f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in symmetricPairs]) + trStr = mathModeListLatex([f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" with ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs]) + + return replaceContent( + (rxStr, smStr, trStr), + 'Relations/EquivalenceProof', + lambda temp, cont: temp.replace('%REFLEXIVE', cont[0]).replace('%SYMMETRIC', cont[1]).replace('%TRANSITIVE', cont[2]) + ) + + return self.isReflexive() and self.isSymmetric() and self.isTransitive() + + def isPartialOrder(self, verbose=False): + + result = self.isReflexive() and self.isAntiSymmetric() and self.isTransitive() + + if result: + hasse= self.getHassePairs(checkIfPartialOrder=False) + min_el = set(a for a,b in hasse if a not in list(zip(*hasse))[1]) + printc(f"Minimal elements: $\{{ {', '.join(str(e) for e in min_el)} \}}$ \\\\") + + max_el = set(v for k,v in hasse if v not in (x for x,_ in hasse)) + printc(f"Maximal elements: $\{{ {', '.join(str(e) for e in max_el)} \}}$" ) + + if verbose: + isReflexive, reflexivePairs = self.isReflexive(verbose=True) + isAntiSymmetric, antiSymmetricPairs = self.isAntiSymmetric(verbose=True) + isTransitive, transitivePairs = self.isTransitive(verbose=True) + + if not isReflexive: + return 'The relation is not a partial order, because it is not reflexive.' + + ' The following elements should be related:\n\n' + + f'\\[ {", ".join(pairToString(p) for p in reflexivePairs)} \\]' + if not isAntiSymmetric: + return 'The relation is not a partial order, because it is not antisymmetric.' + + ' The following relations are symmetric\n\n' + + f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in antiSymmetricPairs)} \\]' + if not isTransitive: + return 'The relation is not a partial order, because it is not transitive.' + + ' The following pairs of relations are missing its transitive counterpart\n\n' + + f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" without ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs)} \\]' + + rxStr = mathModeListLatex([pairToString(p) for p in reflexivePairs]) + trStr = mathModeListLatex([f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" with ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs]) + + return replaceContent( + (rxStr, trStr), + 'Relations/PosetProof', + lambda temp, cont: temp.replace('%REFLEXIVE', cont[0]).replace('%TRANSITIVE', cont[1]) + ) + + return result + + def getHassePairs(self, checkIfPartialOrder=True): + if checkIfPartialOrder and not self.isPartialOrder(): + printerr('This is not a partial order') + return + + nonReflexivePairs = set((x,y) for x,y in self.pairs if x != y) + + hassePairs = set() + for x1, y1 in nonReflexivePairs: + for x2, y2 in nonReflexivePairs: + if y1 == x2: + hassePairs.add((x1, y2)) + return nonReflexivePairs - hassePairs + + def latexifyHasseDiagram(self): + hasse = self.getHassePairs() + keys = set(item for tup in hasse for item in tup) + y_pos = dict.fromkeys(keys, 0) + + i = 0 + while len(next_row := [val for key,val in hasse if key in [x for x,y in y_pos.items() if y == i] ]) != 0: + for item in next_row: + y_pos[item] = i + 1 + i += 1 + + inv_ypos = dict() + for key in set(y_pos.values()): + inv_ypos[key] = [x for x,y in y_pos.items() if y == key] + + output = [] + + for y in inv_ypos.keys(): + for i, n in enumerate(inv_ypos[y]): + output.append(f'\\node ({n}) at ({i - len(inv_ypos[y])/2}, {y * 0.5 * (len(hasse) ** 0.4)}) {{${n}$}};') + + output.append('') + + for x,y in hasse: + output.append(f'\\draw ({x}) -- ({y});') + + return '\n'.join(output) + + def latexifyGraph(self): + if self.isEquivalenceRelation(): + graphType = 'undirected' + pairs = [(x,y) for x,y in self.pairs if x != y] + else: + graphType = 'directed' + pairs = self.pairs + + nodes = set() + for x,y in pairs: + nodes.add(x) + nodes.add(y) + + edges = "\n".join(pairToString(p) for p in pairs) + + processingString = f'toGraph\n\n{graphType}\n\n{ " ".join(nodes) }\n\n{edges}' + return Graph.processFileContent(processingString) + + +def processFileContent(raw): + outputType, inp = raw.split('\n\n') + + if inp.startswith('divisibilityPosetTo'): + n = int(inp.split(' ')[1]) + relation = Relation.fromDivisibilityPosetTo(n) + else: + relation = Relation.fromString(inp) + + if outputType == 'proveEquivalence': + content = relation.isEquivalenceRelation(verbose=True) + return content + + elif outputType == 'provePoset': + content = relation.isPartialOrder(verbose=True) + return content + + elif outputType == 'hasseDiagram': + content = relation.latexifyHasseDiagram() + return replaceContent(content, 'Relations/Hasse') + + elif outputType == 'graph': + content = relation.latexifyGraph() + return content + +if __name__ == '__main__': + pass + relations = [(x,y) for x in range(-5, 5) for y in range(-5, 5)] + relations = [(chr(70+x), chr(70+y)) for x,y in relations if x*y >= 0] + for x in range(-5 ,5): + print(str(x) + ' - '+ chr(70 + x)) + print(Relation(relations).isTransitive()) + # inp = 'AA BB CC DD AB ACj + # relations = [stringToPair(p) for p in inp.split(' ')] + # # print(Relation(relations).isEquivalenceRelation()) + # print(Relation(relations).isPartialOrder()) + # print(Relation.fromDivisibilityPosetTo(30).isPartialOrder()) diff --git a/exam/python/Truthtable.py b/exam/python/Truthtable.py new file mode 100644 index 0000000..3426e63 --- /dev/null +++ b/exam/python/Truthtable.py @@ -0,0 +1,111 @@ +from sys import argv +from pathlib import Path + +from common import printerr, replaceContent + +try: + from tabulate import tabulate +except: + printerr('Couldn\'t find tabulate. Do you have it installed?') + + +def parseExpressionList(inputData): + return [e.strip() for e in inputData.split(',')] + + +class OverloadedBool: + """Overloads the bool in order to make Implies and Iff functions""" + + def __init__(self, val): + self.val = val + + def __bool__(self): + val = self.val + while(type(val) is OverloadedBool): + val = val.val + return val + + def __str__(self): + return str(self.val) + + def __add__(self, bool2): + """ Implies """ + return OverloadedBool(not self.val or bool2) + + def __sub__(self, bool2): + """ Iff """ + return OverloadedBool((not self.val or bool2) and (not bool2 or self.val)) + +def flattenImpliesIff(exps): + return [exp.replace('implies', '+').replace('iff', '-').replace('E', '') for exp in exps] + +def generateTruthTable(exps): + + exps = flattenImpliesIff(exps) + + boolvars = [e for e in exps if len(e) == 1] + + truthtable = [] + + for x in reversed(range(2 ** len(boolvars))): + for i,digit in enumerate('{0:b}'.format(x).zfill(len(boolvars))): + exec(f'{boolvars[i]} = OverloadedBool({digit == "1"})', globals()) + + def calculateGridLine(): + line = [] + for exp in exps: + exec(f'line.append(({exp}))') + return line + + truthtable.append(calculateGridLine()) + + return truthtable + + +def latexify(exps, truthtable): + + latex_expressions = \ + [ ( + 'E' in exp, + exp + .replace('not', '\\neg') + .replace('and', '\\wedge') + .replace('or', '\\vee') + .replace('implies', '\\Rightarrow') + .replace('iff', '\\Leftrightarrow') + .replace('E', '' + .strip()) + ) + for exp in exps] + + return \ + """ + \\begin{{truthtable}} + {{{}}} + {{{}}} +{} + \\end{{truthtable}} + """.format( + '|'.join('e' if e else 'c' for e,_ in latex_expressions), + ' & '.join(f'${exp}$' for _,exp in latex_expressions), + '\n'.join(' ' + ' & '.join('\\T' if b else '\\F' for b in line) + ' \\\\' for line in truthtable) + ) + +def printTruthtable(exps, truthtable): + stringTable = [ ['\033[32mT\033[0m ' if b else '\033[31mF\033[0m' for b in line] for line in truthtable] + try: + print(tabulate(stringTable, headers=exps)) + except: + pass + +def processFileContent(raw): + exps = parseExpressionList(raw) + truthtable = generateTruthTable(exps) + + printTruthtable(exps, generateTruthTable(exps)) + + content = latexify(exps, truthtable) + return replaceContent(content, 'Truthtable') + +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/exam/python/common.py b/exam/python/common.py new file mode 100644 index 0000000..77774a9 --- /dev/null +++ b/exam/python/common.py @@ -0,0 +1,21 @@ +from pathlib import Path + +clear = '\033[0m' +colors = { + 'red': '\033[31m', + 'green': '\033[32m', + 'yellow': '\033[33m', + 'blue': '\033[34m', + 'purple': '\033[35m', + 'cyan': '\033[36m', +} + +def printc(text, color='blue'): + print(f'{colors[color]}{text}{clear}') + +def printerr(text): + print(f'\033[31;5m[ERROR] {text}{clear}') + +def replaceContent(content, template, replaceF = lambda temp, cont: temp.replace("%CONTENT", cont)): + with open(str(Path(__file__).parent.absolute()) + f'/tex_templates/{template}.tex') as template: + return replaceF(template.read(), content) \ No newline at end of file diff --git a/exam/python/run.py b/exam/python/run.py new file mode 100644 index 0000000..d0af2dd --- /dev/null +++ b/exam/python/run.py @@ -0,0 +1,48 @@ +from sys import argv +from pathlib import Path + +import FSA +import Graph +import Truthtable +import Relations +import InclusionExclusion +import Python + +from common import printerr + +def fetchContentType(content): + new_content = content.split('\n') + contentType = new_content.pop(0)[2:] + return (contentType, '\n'.join(new_content)) + +def processContent(content): + contentType, content = fetchContentType(content) + + if contentType == 'FSA': + result = FSA.processFileContent(content) + elif contentType == 'Graph': + result = Graph.processFileContent('toGraph\n\n' + content) + elif contentType == 'Matrix': + result = Graph.processFileContent('toMatrix\n\n' + content) + elif contentType == 'Relations': + result = Relations.processFileContent(content) + elif contentType == 'Truthtable': + result = Truthtable.processFileContent(content) + elif contentType == 'InclusionExclusion': + result = InclusionExclusion.processFileContent(content) + elif contentType == 'Python': + result = Python.processFileContent(content) + else: + printerr('DIDN\'T RECOGNIZE FILE TYPE') + exit(1) + + return result + +if __name__ == '__main__': + filename = argv[1] + + with open(filename) as file: + content = processContent(file.read()) + + with open(argv[2], 'w') as destination_file: + destination_file.write(content) \ No newline at end of file diff --git a/exam/python/tex_templates/FSA.tex b/exam/python/tex_templates/FSA.tex new file mode 100644 index 0000000..23b60ac --- /dev/null +++ b/exam/python/tex_templates/FSA.tex @@ -0,0 +1,12 @@ +\begin{tikzpicture} + \tikzset{ + ->, % makes the edges directed + >=Stealth, % makes the arrow heads bold + node distance=5cm, % specifies the minimum distance between two nodes. Change if necessary. + every state/.style={thick, fill=white}, % sets the properties for each ’state’ node + initial text=$ $, % sets the text that appears on the start arrow + } + +%CONTENT + +\end{tikzpicture} \ No newline at end of file diff --git a/exam/python/tex_templates/Graph.tex b/exam/python/tex_templates/Graph.tex new file mode 100644 index 0000000..7c63fba --- /dev/null +++ b/exam/python/tex_templates/Graph.tex @@ -0,0 +1,13 @@ +\newcommand{\arrow}[2]{\path [-{Latex[scale=1]}] (#1) edge (#2);} + +\begin{tikzpicture} + + \begin{scope}[every node/.style={shape=circle, fill=white, draw, inner sep=2pt}] + %NODES + \end{scope} + + \begin{scope}[every draw/.style={}] + %EDGES + \end{scope} + +\end{tikzpicture} diff --git a/exam/python/tex_templates/Kruskals.tex b/exam/python/tex_templates/Kruskals.tex new file mode 100644 index 0000000..f06b504 --- /dev/null +++ b/exam/python/tex_templates/Kruskals.tex @@ -0,0 +1,37 @@ +\begin{mgraphbox}[width=\linewidth] + \begin{figure}[H] + \begin{center} + \begin{tikzpicture} + + \begin{scope}[every node/.style={shape=circle, fill=white, draw, inner sep=2pt}] + %NODES1 + \end{scope} + + \begin{scope}[every draw/.style={}] + %EDGES1 + \end{scope} + + \end{tikzpicture} + \end{center} + \end{figure} +\end{mgraphbox} + +Minimal spanning tree: + +\begin{mgraphbox}[width=\linewidth] + \begin{figure}[H] + \begin{center} + \begin{tikzpicture} + + \begin{scope}[every node/.style={shape=circle, fill=white, draw, inner sep=2pt}] + %NODES2 + \end{scope} + + \begin{scope}[every draw/.style={}] + %EDGES2 + \end{scope} + + \end{tikzpicture} + \end{center} + \end{figure} +\end{mgraphbox} \ No newline at end of file diff --git a/exam/python/tex_templates/Matrix.tex b/exam/python/tex_templates/Matrix.tex new file mode 100644 index 0000000..cf0685b --- /dev/null +++ b/exam/python/tex_templates/Matrix.tex @@ -0,0 +1,5 @@ +\begin{figure}[H] + \[ + %CONTENT + \] +\end{figure} \ No newline at end of file diff --git a/exam/python/tex_templates/Powerset.tex b/exam/python/tex_templates/Powerset.tex new file mode 100644 index 0000000..e69de29 diff --git a/exam/python/tex_templates/Python.tex b/exam/python/tex_templates/Python.tex new file mode 100644 index 0000000..6a9fdc1 --- /dev/null +++ b/exam/python/tex_templates/Python.tex @@ -0,0 +1,8 @@ + +\codeFile{%CODEFILE}{python} + +Output: + +\begin{lstlisting}[breaklines=true, basicstyle=\small] +%OUTPUT +\end{lstlisting} \ No newline at end of file diff --git a/exam/python/tex_templates/Relations/EquivalenceProof.tex b/exam/python/tex_templates/Relations/EquivalenceProof.tex new file mode 100644 index 0000000..46a7ad7 --- /dev/null +++ b/exam/python/tex_templates/Relations/EquivalenceProof.tex @@ -0,0 +1,22 @@ + +In order for this relation to be an equivalence equation, it has to be reflexive, symmetric and transitive. + +\textbf{Reflexive:} + +All elements are related to themself + +%REFLEXIVE + +\textbf{Symmetric:} + +All relations has its symmetric counterpart + +%SYMMETRIC + +\textbf{Transitive:} + +All pair of relations where $xRy$ and $yRz$ has its transitive counterpart + +%TRANSITIVE + +Hence the relation is an equivalence relation \ No newline at end of file diff --git a/exam/python/tex_templates/Relations/Hasse.tex b/exam/python/tex_templates/Relations/Hasse.tex new file mode 100644 index 0000000..d043b63 --- /dev/null +++ b/exam/python/tex_templates/Relations/Hasse.tex @@ -0,0 +1,6 @@ +\begin{tikzpicture} + \tikzset{every node/.style={shape=circle,draw,fill=white,inner sep=2pt}} + +%CONTENT + +\end{tikzpicture} \ No newline at end of file diff --git a/exam/python/tex_templates/Relations/PosetProof.tex b/exam/python/tex_templates/Relations/PosetProof.tex new file mode 100644 index 0000000..f168ced --- /dev/null +++ b/exam/python/tex_templates/Relations/PosetProof.tex @@ -0,0 +1,22 @@ + +In order for this relation to be a partial order, it has to be reflexive, antisymmetric and transitive. + +\textbf{Reflexive:} + +All elements are related to themself + +%REFLEXIVE + +\textbf{Antisymmetric:} + +No relation have a symmetric counterpart \\ + +(Listing the ones that don't have a symmetric counterpart would just be listing the whole set) \\ + +\textbf{Transitive:} + +All pair of relations where $xRy$ and $yRz$ has its transitive counterpart + +%TRANSITIVE + +Hence the relation is a partial order diff --git a/exam/python/tex_templates/Truthtable.tex b/exam/python/tex_templates/Truthtable.tex new file mode 100644 index 0000000..6c7f444 --- /dev/null +++ b/exam/python/tex_templates/Truthtable.tex @@ -0,0 +1 @@ +%CONTENT \ No newline at end of file