summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsyn <isaqtm@gmail.com>2020-02-16 11:56:52 +0300
committersyn <isaqtm@gmail.com>2020-02-16 11:56:52 +0300
commitfd78d0fd00e6b98ae8b9894db8be028575e205ac (patch)
treeee9dd35e871d222aa60a1958e9b6f28eaa6ecc54
downloadalg3-fd78d0fd00e6b98ae8b9894db8be028575e205ac.tar.gz
Init commit
-rw-r--r--README2
-rw-r--r--four.py55
-rw-r--r--gen_all.py2
-rw-r--r--hw3.tex134
-rw-r--r--intro.tex97
-rw-r--r--libsolve.py330
-rw-r--r--one.py114
-rw-r--r--render.py45
-rw-r--r--two.py107
9 files changed, 886 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..8c9d5ba
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+Я стал еще более ленивым, поэтому многие матрицы в пдфке написаны не совсем в пдфке.
+Их надо сгенерить скриптом (gen_all.py)
diff --git a/four.py b/four.py
new file mode 100644
index 0000000..4f7cd54
--- /dev/null
+++ b/four.py
@@ -0,0 +1,55 @@
+from libsolve import *
+from fractions import Fraction
+import numpy as np
+
+FOUR_SIMEQ = '\\overset{{\\texttt{{two.py}}}}\\simeq'
+
+# generate figure as plain file, so we can \input it
+def gen_figure(figname, text):
+ with open(f'figures/fig_four_{figname}.tex', 'w') as fd:
+ fd.write('$$' + text + '$$')
+
+A = Matrix([
+ [-1, 1, 1, -1],
+ [-7, 4, 4, -3],
+ [4, -1, -2, 1],
+ [4, -1, -2, 1]
+])
+
+lbdE = Matrix([
+ [Poly([0, 1]), 0, 0, 0],
+ [0, Poly([0, 1]), 0, 0],
+ [0, 0, Poly([0, 1]), 0],
+ [0, 0, 0, Poly([0, 1])]
+])
+
+# uncomment to see characteristic poly
+#print((A - lbdE).det())
+
+
+
+A = A - Matrix([
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+])
+
+A = A@A
+
+before = A.to_tex()
+
+A.triangulate()
+
+A.make_D(0, -1)
+A.make_U(1, 2)
+A.make_D(1, Fraction(1, 4))
+
+gen_figure('fsr_A_T',
+f'''
+ {before}
+ {FOUR_SIMEQ}
+ {A.to_tex()}
+''')
+
+print(A) \ No newline at end of file
diff --git a/gen_all.py b/gen_all.py
new file mode 100644
index 0000000..07a1c0f
--- /dev/null
+++ b/gen_all.py
@@ -0,0 +1,2 @@
+import one
+import two
diff --git a/hw3.tex b/hw3.tex
new file mode 100644
index 0000000..2ec8de5
--- /dev/null
+++ b/hw3.tex
@@ -0,0 +1,134 @@
+\documentclass[10pt,a5paper]{article}
+\usepackage[svgnames, rgb]{xcolor}
+
+\input{intro}
+
+\lhead{\color{gray} Шарафатдинов Камиль 192}
+\rhead{\color{gray} \texttt{hw3}}
+\title{ИДЗ-3 по линейной алгебре}
+\author{Шарафатдинов Камиль БПМИ-192}
+\date{билд: \today}
+
+
+% -- Here bet dragons --
+\begin{document}\thispagestyle{empty}
+
+\maketitle
+\clearpage
+\setcounter{page}{1}
+
+\dmquestion{1}
+
+ Проверим, что векторы из условия являются решением системы
+ \input{figures/fig_one_proof_v0.tex}
+ \input{figures/fig_one_proof_v0.tex}
+
+
+ Найдем ФСР системы:
+ \input{figures/fig_one_init_to_triang}
+
+ ФСР:
+ \begin{flalign*}
+ \lambda_2 = 1 \qquad& (4, 1, 0, 0, 0, 0)\\
+ \lambda_4 = 1 \qquad& (-1, 0, 1, 1, 0, 0)\\
+ \lambda_5 = 1 \qquad& (-1, 0, -2, 0, 2, 0)\\
+ \lambda_6 = 1 \qquad& (1, 0, -2, 0, 0, 1)
+ \end{flalign*}
+
+ Выразим векторы $v_1$ и $v_2$ (из условия) через ФСР (мы так можем, потому что ФСР - базис этого подпространства)
+
+ Для этого нужно решить две системы:
+ \input{figures/fig_one_convert_v1}
+ \[
+ u_1 = (1, -3, 3, -1)
+ \]
+ \input{figures/fig_one_convert_v2}
+ \[
+ u_2 = (2, 3, 1, -2)
+ \]
+
+ Теперь надо дополнить эту систему до базиса $\mathbb{R}^4$:
+ \input{figures/fig_one_solve_last}
+
+ Дополнить до базиса можно векторами $(0, 0, 1, 0)$ и $(0, 0, 0, 1)$
+
+ То есть последние два вектора из ФСР - дополнение до базиса подпространства.
+
+ \begin{flalign*}
+ (-1, 0, -2, 0, 2, 0)\\
+ (1, 0, -2, 0, 0, 1)
+ \end{flalign*}
+
+\dmquestion{2}
+
+ $\Image \varphi$ - линейная оболочка столбцов.
+ Найдем матрицу, задающую $\Image \varphi$ по алгоритму от Димы:
+
+ Найдем ФСР $A^Tx = 0$:
+ \input{figures/fig_two_fsr_A_T}
+
+ ФСР:
+ \[
+ (2, -1, 1, 0), \quad \br{ \frac{7}{5}, -\frac{4}{5}, 0, 1 } = (7, -4, 0, 5)
+ \]
+
+ Значит, $\Image \varphi = \left\{ x \ | \ Bx = 0\right\}$, где
+ \[
+ B = \begin{bmatrix}
+ 2 & -1 & 1 & 0\\
+ 7 & -4 & 0 & 5
+ \end{bmatrix}
+ \]
+
+ Тогда $\Image \varphi \cap \ker \varphi = \left\{x | Ax = 0, Bx = 0\right\}$
+
+ \input{figures/fig_two_AB_sol}
+
+ ФСР такой системы - $(-3, -4, 2, 1)$
+
+ Значит, $\Image \varphi \cap \ker \varphi = \langle(-3, -4, 2, 1)\rangle$
+
+ $\Image \varphi$ - линейная оболочка столбцов,
+ а $\ker \varphi$ - линейная оболочка ФСР $Ax = 0$.
+ Тогда $\Image \varphi + \ker \varphi$ - линейная оболочка всех этих векторов.
+ Надо только найти в них базис:
+
+ \input{figures/fig_two_ker_A}
+
+ $
+ \ker \varphi = \langle (-\frac{3}{2}, -3, 1, 0), (0, 2, 0, 1) \rangle
+ =
+ \ker \varphi = \langle (-3, -6, 2, 0), (0, 2, 0, 1) \rangle
+ $
+
+ Теперь надо найти базис из столбцов $A$ и базиса в $\ker \varphi$:
+
+ \input{figures/fig_two_imker}
+
+ Ненулевые строки - базис (возьмем векторы из левой матрицы).
+
+ Поэтому
+ $
+ \Image \varphi + \ker \varphi =
+ \langle
+ (-2, 14, 18, 14),
+ (-3, -9, -3, -3),
+ (-3, -6, 2, 0)
+ \rangle
+ $
+
+%\dmquestion{4}
+% Найдем хар. многочлен $\varphi$:
+% \[
+% \mathrm{det} (\varphi - \lambda E)
+% \overset{\texttt{four.py}}=
+% \lambda^4 - \lambda^3 + \lambda^2 =
+% \lambda^2(\lambda - 1)^2
+% \]
+%
+% $
+% V^1 = \ker ((\varphi - E)^2)
+% $, так как у каждого собственного значения степень 2
+% \input{figures/fig_four_fsr_A_T}
+
+\end{document}
diff --git a/intro.tex b/intro.tex
new file mode 100644
index 0000000..f4f9d67
--- /dev/null
+++ b/intro.tex
@@ -0,0 +1,97 @@
+\usepackage[sfdefault,condensed,scaled=0.9]{roboto}
+\usepackage{inconsolata}
+\setmonofont[Scale=0.9]{Inconsolata}
+
+\setlength\headheight{13.6pt}
+
+\usepackage{
+ amsmath, amssymb, mathtools, relsize,
+ graphicx, xcolor,
+ fancyhdr, hyperref, enumerate, framed
+}
+\usepackage[shortlabels]{enumitem}
+
+\flushbottom
+
+\renewcommand{\baselinestretch}{1.1}
+
+\usepackage{geometry}\geometry{letterpaper, % Set page margins
+ left=0.7in, right=0.7in,
+ top=0.8in, bottom=0.9in,
+ headsep=.1in
+}
+
+\setlength\FrameSep{0.75em}
+\setlength\OuterFrameSep{\partopsep}
+
+\newenvironment{cframed}[1][gray]
+{
+ \def\FrameCommand{
+ \fboxsep=\FrameSep\fcolorbox{#1}{white}
+ }
+ \MakeFramed{
+ \advance\hsize-\width \FrameRestore
+ }
+}
+{\endMakeFramed}
+
+\makeatletter
+\newenvironment{sqcases}{%
+ \matrix@check\sqcases\env@sqcases
+}{%
+ \endarray\right.%
+}
+\def\env@sqcases{%
+ \let\@ifnextchar\new@ifnextchar
+ \left\lbrack
+ \def\arraystretch{1.2}%
+ \array{@{}l@{\quad}l@{}}%
+}
+\makeatother
+
+\DeclareRobustCommand{\divby}{%
+ \mathrel{\vbox{\baselineskip.65ex\lineskiplimit0pt\hbox{.}\hbox{.}\hbox{.}}}%
+}
+
+\newcommand{\question}[2]{
+ \bigskip\bigskip
+ \begin{cframed}
+ \noindent \textbf{#1}
+ #2
+ \end{cframed}
+}
+
+\newcommand{\dmquestion}[1]{
+ \begin{center} \textbf{#1} \end{center}
+}
+
+\newcommand{\br}[1]{\left( #1 \right)}
+\newcommand{\brac}[2]{ \br{ \frac{#1}{#2} } }
+\newcommand{\dbrac}[2]{ \br{ \dfrac{#1}{#2} } }
+\newcommand*{\qed}{\hfill\ensuremath{\blacksquare}}
+\newcommand*{\qedempty}{\hfill\ensuremath{\square}}
+
+\newcommand{\explain}[1]{
+ \begin{bmatrix}
+ #1
+ \end{bmatrix}
+}
+
+\newcommand{\probability}[1]{\mathrm{Pr} \left[ #1 \right]}
+\newcommand{\expected}[1]{\mathrm{E} \left[ #1 \right]}
+
+\newcommand{\todo}{\texttt{todo!}}
+
+\newcommand{\osmall}[1]{\overline{o}\left( #1 \right)}
+\DeclareMathOperator{\dif}{d \!}
+\DeclareMathOperator{\Image}{Im}
+
+\hypersetup{colorlinks=true, linkcolor=magenta}
+
+% -- Left/right header text and footer (to appear on every page) --
+\pagestyle{fancy}
+\renewcommand{\footrulewidth}{0.4pt}
+\renewcommand{\headrulewidth}{0.4pt}
+
+\cfoot{}
+\rfoot{\thepage}
diff --git a/libsolve.py b/libsolve.py
new file mode 100644
index 0000000..e9a0677
--- /dev/null
+++ b/libsolve.py
@@ -0,0 +1,330 @@
+from __future__ import annotations
+from typing import Iterable, Union
+from fractions import Fraction
+from itertools import zip_longest
+
+
+def is_scalar(obj):
+ return isinstance(obj, (Fraction, int))
+
+
+POLY_COLOR = None
+# POLY_COLOR = '36' # Uncomment to have color
+
+
+class PolyRenderBase:
+ def render(self, poly: Poly) -> str:
+ raise NotImplementedError()
+
+ def hint(self, poly: Poly) -> int:
+ raise NotImplementedError()
+
+
+class Poly:
+ def __init__(self, vec: Iterable, letter='x', renderer=None):
+ '''
+ vec: big-endian coefficients
+ '''
+ from render import UnicodeRender
+ self.vec = list(vec)
+ self.letter = letter
+ self.renderer = renderer or UnicodeRender()
+ self._trim_zeros()
+
+ def _trim_zeros(self):
+ '''
+ Trim trailing zeros in coefficients: [0, 1, 1, 0] -> [0, 1, 1]
+ Needed for multiplication to work correctly
+ '''
+ len_zeros = 0
+ for c in reversed(self.vec):
+ if c != 0:
+ break
+ len_zeros += 1
+ if len_zeros > 0:
+ self.vec = self.vec[:-len_zeros]
+ if len(self.vec) == 0:
+ self.vec = [0]
+
+ def __mul__(self, rhs: Union[Poly, Fraction, int]):
+ if is_scalar(rhs):
+ return Poly([c * rhs for c in self.vec])
+
+ elif isinstance(rhs, Poly):
+ # no fft 4 u
+ result = [0] * (self.deg() + rhs.deg() + 1)
+ self._trim_zeros()
+ rhs._trim_zeros()
+ for i in range(len(self.vec)):
+ for j in range(len(rhs.vec)):
+ if all(c != 0 for c in (self.vec[i], rhs.vec[j])):
+ result[i + j] += self.vec[i] * rhs.vec[j]
+ return Poly(result)
+
+ else:
+ raise TypeError(f'{type(rhs)} not supported')
+
+ def __truediv__(self, rhs: Union[Fraction, int]):
+ if is_scalar(rhs):
+ return Poly([c * Fraction(1, rhs) for c in self.vec])
+ else:
+ raise TypeError(f'{type(rhs)} not supported')
+
+ def __add__(self, rhs: Union[Poly, Fraction, int]):
+ if is_scalar(rhs):
+ return Poly([self.vec[0] + rhs] + self.vec[1:])
+ elif isinstance(rhs, Poly):
+ result = []
+ for pair in zip_longest(self.vec, rhs.vec):
+ result.append(sum(c for c in pair if c is not None))
+ return Poly(result)
+
+ def __sub__(self, rhs: Union[Poly, Fraction, int]):
+ return self + (-rhs)
+
+ def __neg__(self):
+ return Poly(-c for c in self.vec)
+
+ def deg(self):
+ '''
+ Get degree of poly
+ '''
+ d = len(self.vec) - 1
+ for c in reversed(self.vec):
+ if c != 0:
+ return d
+ else:
+ d -= 1
+ return max(d, 0)
+
+ def shift(self, deg):
+ '''
+ divide by x^deg, drop rest
+ '''
+ for i in range(deg):
+ del self.vec[0]
+
+ def __repr__(self):
+ return self.renderer.render(self)
+
+ def hint(self):
+ return self.renderer.hint(self)
+
+
+class Row:
+ '''
+ Matrix row.
+ '''
+
+ def __init__(self, it: Iterable, dtype=Poly):
+ def make_poly(obj):
+ if isinstance(obj, (int, Fraction)):
+ return Poly([obj])
+ elif isinstance(obj, Poly):
+ return obj
+ else:
+ raise TypeError(f'{type(obj)} not supported')
+
+ self.lst = list(map(make_poly, it))
+ self.hints = [1] * len(self.lst)
+
+ def __add__(self, obj: Row):
+ '''
+ Add another row
+ '''
+ assert isinstance(obj, Row)
+ assert len(obj.lst) == len(self.lst)
+ pairs = zip(self.lst, obj.lst)
+ return Row(map(lambda x: x[0] + x[1], pairs))
+
+ def __mul__(self, k: Union[int, Fraction]):
+ '''
+ Multiply by int or fractions.Fraction
+ '''
+ assert isinstance(k, (int, Fraction))
+ return Row(map(lambda x: x * k, self.lst))
+
+ def __iter__(self):
+ return iter(self.lst)
+
+ def __getitem__(self, key):
+ return self.lst[key]
+
+ def __setitem__(self, key, val):
+ self.lst[key] = val
+
+ def __len__(self):
+ return len(self.lst)
+
+ def __repr__(self):
+ parts = []
+ for el, hint in zip(self.lst, self.hints):
+ part = '{el: >{hint}}'.format(el=repr(el), hint=hint)
+ if el.deg() > 0 and POLY_COLOR is not None:
+ part = '\x1b[' + POLY_COLOR + 'm' + part + '\x1b[0m'
+ parts.append(part)
+
+ return ('[' + ' '.join(parts) + ']')
+
+ def get_hints(self):
+ '''
+ Get hints for other structures to align items
+ '''
+ return [el.hint() for el in self.lst]
+
+ def set_hints(self, hints):
+ '''
+ Set hints to align items within row
+ '''
+ self.hints = hints
+
+
+class Matrix:
+ '''
+ Matrix. You can apply three elementary transforms to self.
+ '''
+
+ def __init__(self, rows):
+ def make_row(obj):
+ if isinstance(obj, Row):
+ return obj
+ else:
+ return Row(obj)
+
+ self.rows = list(map(make_row, rows))
+ assert all(len(row) == len(self.rows[0]) for row in self.rows)
+
+ def make_S(self, i: int, j: int, lbd: Union[int, Fraction], axis=0):
+ '''
+ if axis == 0, do transform on rows, else on cols
+ M[i] = M[i] + M[j] * lbd
+ '''
+ if axis == 0:
+ self.rows[i] = self.rows[i] + self.rows[j] * lbd
+ else:
+ for row in self.rows:
+ row[i] = row[i] + row[j] * lbd
+
+ def make_U(self, i: int, j: int, axis=0):
+ '''
+ if axis == 0, do transform on rows, else on cols
+ Swap M[i] and M[j]
+ '''
+ if axis == 0:
+ self.rows[i], self.rows[j] = self.rows[j], self.rows[i]
+ else:
+ for row in self.rows:
+ row[i], row[j] = row[j], row[i]
+
+ def make_D(self, i: int, lbd: Union[int, Fraction], axis=0):
+ '''
+ if axis == 0, do transform on rows, else on cols
+ Multiply M[i] by rational lbd
+ '''
+ if axis == 0:
+ self.rows[i] = self.rows[i] * lbd
+ else:
+ for row in self.rows:
+ row[i] = row[i] * lbd
+
+ def det(self) -> Poly:
+ '''
+ Get determinant of matrix
+ '''
+ assert all(len(row) == len(self.rows) for row in self.rows)
+ if len(self.rows) == 1:
+ return self.rows[0][0]
+ res = Poly([0])
+ try:
+ for i in range(len(self.rows[0])):
+ cofactor = Matrix([
+ self.rows[j][:i] + self.rows[j][i + 1:]
+ for j in range(1, len(self.rows))
+ ])
+ res += cofactor.det() * (-1) ** i * self.rows[0][i]
+ except Exception as e:
+ print(self)
+ raise e
+
+ return res
+
+ def __sub__(self, oth):
+ return self + (-oth)
+
+ def __neg__(self):
+ rows = [row * (-1) for row in self.rows]
+ return Matrix(rows)
+
+ def __add__(self, oth):
+ rows = [row1 + row2 for row1, row2 in zip(self.rows, oth.rows)]
+ return Matrix(rows)
+
+ def __getitem__(self, idx):
+ return self.rows[idx]
+
+ def __setitem__(self, idx, val):
+ self.rows[idx] = val
+
+ @property
+ def shape(self):
+ return len(self.rows), len(self.rows[0])
+
+ def __matmul__(self, other):
+ assert self.shape[1] == other.shape[0]
+ rows = [
+ [
+ sum((
+ self.rows[j][i] * other.rows[i][k]
+ for i in range(self.shape[1])
+ ), Poly([0]))
+ for k in range(other.shape[1])
+ ] for j in range(self.shape[0])
+ ]
+ return Matrix(rows)
+
+ def __repr__(self):
+ hints = [row.get_hints() for row in self.rows]
+ max_hints = [
+ max(hints[j][i] for j in range(len(hints)))
+ for i in range(len(hints[0]))
+ ]
+
+ for row in self.rows:
+ row.set_hints(max_hints)
+
+ return '\n'.join(repr(row) for row in self.rows)
+
+ def to_tex(self):
+ parts = []
+ for row in self.rows:
+ parts.append(' & '.join(map(str, row.lst)))
+ return '\\begin{bmatrix}\n' + '\\\\\n'.join(parts) + '\n\\end{bmatrix}'
+
+ def triangulate(self):
+ for i in range(self.shape[1]):
+ for j in range(i, self.shape[0]):
+ assert(self[j][i].deg() == 0)
+ if self[j][i].vec[0] != 0:
+ # check we are main variable
+ if not all(self[j][k].vec[0] == 0 for k in range(i)):
+ continue
+
+ for k in range(self.shape[0]):
+ assert(self[k][i].deg() == 0)
+ if k == j:
+ continue
+ coef = -Fraction(self[k][i].vec[0]) / Fraction(self[j][i].vec[0])
+ self[k] = self[k] + self[j] * coef
+ break
+
+class Permutation:
+ def __init__(self, perm: Iterable):
+ self.perm = list(perm)
+
+ def apply(self, now: list):
+ assert len(self.perm) == len(now)
+ res = [0] * len(self.perm)
+ for i in range(len(self.perm)):
+ res[self.perm[i]] = now[i]
+
+ return res
diff --git a/one.py b/one.py
new file mode 100644
index 0000000..854164e
--- /dev/null
+++ b/one.py
@@ -0,0 +1,114 @@
+from libsolve import *
+from fractions import Fraction
+import numpy as np
+
+ONE_SIMEQ = '\\overset{{\\texttt{{one.py}}}}\\simeq'
+
+# generate figure as plain file, so we can \input it
+def gen_figure(figname, text):
+ with open(f'figures/fig_one_{figname}.tex', 'w') as fd:
+ fd.write('$$' + text + '$$')
+
+m = Matrix([
+ [-1, 4, 1, -2, 1, 3],
+ [-1, 4, 1, -2, 1, 3],
+ [1, -4, 3, -2, 7, 5]
+])
+
+
+vectors = [
+ [3, 1, -7, -3, 3, -1],
+ [2, 2, 5, 3, 1, -2],
+]
+for i, v in enumerate(vectors):
+ v_t = Matrix([[i] for i in [3, 1, -7, -3, 3, -1]])
+ gen_figure(f'proof_v{i}',
+ f'''
+ {m.to_tex()}
+ \\times
+ {v_t.to_tex()}
+ =
+ {(m @ v_t).to_tex()}
+ ''')
+
+init = m.to_tex()
+m.make_S(0, 1, -1)
+m.make_S(1, 2, 1)
+m.make_D(1, Fraction(1, 4))
+m.make_S(2, 1, -3)
+
+init_triang = m.to_tex()
+
+gen_figure('init_to_triang',
+f'''
+ {init}
+ {ONE_SIMEQ}
+ {init_triang}
+''')
+
+
+
+
+
+# steps are the same every time, cause we change only last col
+def convert_steps(mat, mat_no):
+ before = mat.to_tex()
+ mat.make_S(0, 1, -4)
+ mat.make_S(2, 0, 1)
+ mat.make_S(3, 0, 1)
+ mat.make_D(0, -1)
+ mat.make_S(0, 4, -1)
+ mat.make_S(2, 4, 3)
+ mat.make_S(3, 4, 1)
+
+ mat.make_S(0, 5, 1)
+ mat.make_S(2, 5, 1)
+ mat.make_S(3, 5, -1)
+ after = mat.to_tex()
+
+ gen_figure(f'convert_v{mat_no}',
+ f'''
+ {before}
+ {ONE_SIMEQ}
+ {after}
+ ''')
+
+convert_steps(Matrix([
+ [4,-1,-1,1,3],
+ [1,0,0,0,1],
+ [0,1,-2,-2,-7],
+ [0,1,0,0,-3],
+ [0,0,1,0,3],
+ [0,0,0,1,-1]
+]), 1)
+
+
+convert_steps(Matrix([
+ [4,-1,-1,1,2],
+ [1,0,0,0,2],
+ [0,1,-2,-2,5],
+ [0,1,0,0,3],
+ [0,0,1,0,1],
+ [0,0,0,1,-2]
+]), 2)
+
+
+
+
+
+
+m = Matrix([
+ [1, -3, 3, 1],
+ [2, 3, 1, -2]
+])
+
+before = m.to_tex()
+m.make_S(1, 0, -2)
+after = m.to_tex()
+
+gen_figure('solve_last',
+f'''
+ {before}
+ {ONE_SIMEQ}
+ {after}
+''')
diff --git a/render.py b/render.py
new file mode 100644
index 0000000..6cd3c7d
--- /dev/null
+++ b/render.py
@@ -0,0 +1,45 @@
+from libsolve import Poly, PolyRenderBase
+
+
+class UnicodeRender(PolyRenderBase):
+ def __init__(self):
+ # may not work. see
+ # https://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts
+ # to test with your font
+ self.sup = '⁰¹²³⁴⁵⁶⁷⁸⁹' # must be <sup>0123456789</sup>
+
+ def render(self, poly: Poly) -> str:
+ rev = list(reversed(poly.vec))
+ if all(r == 0 for r in rev):
+ return '0'
+ d = poly.deg()
+ res = []
+ for i in range(len(rev)):
+ if rev[i] == 0:
+ continue
+ if i == 0:
+ if rev[i] < 0:
+ res.append('-')
+ else:
+ if rev[i] < 0:
+ res.append(' - ')
+ else:
+ res.append(' + ')
+
+ if abs(rev[i]) != 1 or i == d:
+ res.append(str(abs(rev[i])))
+ if i != d:
+ res.append(poly.letter)
+ if d - i != 1:
+ res.append(self._int2sup(d - i))
+ return ''.join(res)
+
+ def hint(self, poly: Poly) -> int:
+ return len(self.render(poly))
+
+ def _int2sup(self, n):
+ res = []
+ while n > 0:
+ res.append(self.sup[n % 10])
+ n //= 10
+ return ''.join(reversed(res))
diff --git a/two.py b/two.py
new file mode 100644
index 0000000..b5e8b86
--- /dev/null
+++ b/two.py
@@ -0,0 +1,107 @@
+from libsolve import *
+from fractions import Fraction
+import numpy as np
+
+TWO_SIMEQ = '\\overset{{\\texttt{{two.py}}}}\\simeq'
+
+# generate figure as plain file, so we can \input it
+def gen_figure(figname, text):
+ with open(f'figures/fig_two_{figname}.tex', 'w') as fd:
+ fd.write('$$' + text + '$$')
+
+AT = Matrix([
+ [-2, 14, 18, 14],
+ [-3, -9, -3, -3],
+ [-12, -6, 18, 12],
+ [6, 18, 6, 6]
+])
+
+
+before = AT.to_tex()
+
+AT.make_D(0, -Fraction(1, 2))
+AT.make_D(1, -Fraction(1, 3))
+AT.make_D(2, -Fraction(1, 6))
+AT.make_D(3, Fraction(1, 6))
+
+AT.make_S(1, 0, -1)
+AT.make_S(2, 0, -2)
+AT.make_S(3, 0, -1)
+
+AT.make_S(2, 1, -Fraction(3, 2))
+AT.make_S(3, 1, -1)
+
+AT.make_D(1, Fraction(1, 2))
+
+gen_figure('fsr_A_T',
+f'''
+ {before}
+ {TWO_SIMEQ}
+ {AT.to_tex()}
+''')
+
+
+AB = Matrix([
+ [-2, -3, -12, 6],
+ [14, -9, -6, 18],
+ [18, -3, 18, 6],
+ [14, -3, 12, 6],
+ [2, -1, 1, 0],
+ [7, -4, 0, 5]
+])
+
+before = AB.to_tex()
+
+AB.triangulate()
+
+AB.make_D(0, Fraction(1, 2))
+AB.make_D(1, -Fraction(1, 30))
+AB.make_U(2, 4)
+
+
+gen_figure('AB_sol',
+f'''
+{before}
+{TWO_SIMEQ}
+{AB.to_tex()}
+''')
+
+
+A = Matrix([
+ [-2, -3, -12, 6],
+ [14, -9, -6, 18],
+ [18, -3, 18, 6],
+ [14, -3, 12, 6]
+])
+
+before = A.to_tex()
+A.triangulate()
+
+A.make_D(0, -1)
+A.make_D(1, -Fraction(1, 30))
+
+gen_figure('ker_A',
+f'''
+ {before}
+ {TWO_SIMEQ}
+ {A.to_tex()}
+''')
+
+imker = Matrix([
+ [-2, 14, 18, 14],
+ [-3, -9, -3, -3],
+ [-12, -6, 18, 12],
+ [6, 18, 6, 6],
+ [-3, -6, 2, 0],
+ [0, 2, 0, 1]
+])
+
+before = imker.to_tex()
+imker.triangulate()
+
+gen_figure('imker',
+f'''
+ {before}
+ {TWO_SIMEQ}
+ {imker.to_tex()}
+''') \ No newline at end of file