Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5b703efe8
|
||
|
|
4a69fb5e62
|
||
|
|
908ae6b779
|
||
|
|
d68352ba9b
|
||
|
|
d1bece9cfe
|
||
|
|
182d44d2f3
|
||
|
|
27bc87cf5c
|
||
|
|
82dc2545b9
|
||
|
|
74398852d6
|
||
|
|
a2ebfb435c
|
||
|
|
032f3690d2
|
||
|
|
76c39a7c2e
|
||
|
|
56280ee009
|
||
|
|
520148babf
|
||
|
|
76bed19106
|
||
|
|
e73156638b
|
||
|
|
49ae884b09
|
||
|
|
8d51bfaff2
|
||
|
|
2ec86366f2
|
||
|
|
e21fbbe53e
|
||
|
|
83a821a838
|
||
|
|
7fb8db542a
|
@@ -6,6 +6,10 @@ set shell := ["bash", "-uc"]
|
||||
outdir := "out"
|
||||
report_out_path := outdir / "report.pdf"
|
||||
summary_out_path := outdir / "summary.pdf"
|
||||
poster_out_path := outdir / "poster.pdf"
|
||||
presentation_out_path := outdir / "presentation.pdf"
|
||||
presentation_handout_path := outdir / "presentation_handout.pdf"
|
||||
presentation_pdfpc_path := outdir / "presentation.pdfpc"
|
||||
|
||||
default: all
|
||||
|
||||
@@ -35,4 +39,17 @@ report: _setup
|
||||
summary: _setup
|
||||
typst c --root . summary/exec_summary.typ {{summary_out_path}}
|
||||
|
||||
all: report summary
|
||||
poster: _setup
|
||||
typst c --root . poster/poster.typ {{poster_out_path}}
|
||||
|
||||
presentation: _setup
|
||||
typst c --root . presentation/presentation.typ {{presentation_out_path}}
|
||||
typst eval --root . 'query(<pdfpc-file>).first().value' --in presentation/presentation.typ > {{presentation_pdfpc_path}}
|
||||
|
||||
presentation-handout: _setup
|
||||
typst c --root . --input handout=true presentation/presentation.typ {{presentation_handout_path}}
|
||||
|
||||
present: presentation
|
||||
pdfpc {{presentation_out_path}}
|
||||
|
||||
all: report summary poster presentation presentation-handout
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
@@ -0,0 +1,26 @@
|
||||
import qrcode
|
||||
import qrcode.constants
|
||||
from qrcode.image.svg import SvgPathImage
|
||||
|
||||
|
||||
def main():
|
||||
urls: dict[str, str | tuple[str, int | None]] = {
|
||||
"gitea": "https://git.kb28.ch/HEL/midas",
|
||||
"github": "https://github.com/LordBaryhobal/midas",
|
||||
"docs": "https://git.kb28.ch/HEL/TB-Docs",
|
||||
}
|
||||
|
||||
factory = SvgPathImage
|
||||
for name, url in urls.items():
|
||||
version: int | None = 4
|
||||
if isinstance(url, tuple):
|
||||
url, version = url
|
||||
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_Q, version=version)
|
||||
qr.add_data(url)
|
||||
qr.make()
|
||||
img = qr.make_image(image_factory=factory)
|
||||
img.save(f"qr_{name}.svg")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,207 @@
|
||||
// ___ ____ ____ _ _ _____ ___
|
||||
// |_ _/ ___| / ___| | | | | ____|_ _| Informatique et
|
||||
// | |\___ \| | ___ | |_| | _| | | systèmes de communication
|
||||
// | | ___) | |__|___| | _ | |___ | | HEI Sion · HES-SO Valais / mui 24-26
|
||||
// |___|____/ \____| |_| |_|_____|___|
|
||||
//
|
||||
// 52 65 61 64 69 6e 67 20 68 65 78 20 66 6f 72 20 66 75 6e 3f 20 49 53 43 20 66 6f 72 65 76 65 72
|
||||
//
|
||||
|
||||
#import "@preview/isc-hei-poster:0.8.1": isc-poster, isc-card, isc-colbreak
|
||||
#import "@preview/cetz:0.5.2": canvas, draw
|
||||
#import "../meta.typ"
|
||||
#import "../report/figs/architecture.typ"
|
||||
#import "@local/codly:1.3.1": codly, codly-init, local
|
||||
#import "@preview/codly-languages:0.1.10": codly-languages
|
||||
|
||||
#let poster-orientation = "portrait"
|
||||
|
||||
#show: codly-init
|
||||
#codly(
|
||||
languages: codly-languages
|
||||
+ (
|
||||
midas: (
|
||||
name: "Midas",
|
||||
color: rgb("#eedd47"),
|
||||
icon: box(
|
||||
image(
|
||||
"../assets/icon.svg",
|
||||
height: 130%,
|
||||
fit: "contain",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#set raw(
|
||||
syntaxes: path("../midas.sublime-syntax")
|
||||
)
|
||||
#show raw.where(block: true): set text(size: 0.9em)
|
||||
|
||||
/*
|
||||
#show columns: it => {
|
||||
it
|
||||
|
||||
place(
|
||||
top + center,
|
||||
//dx: 0cm,
|
||||
//dy: 21cm,
|
||||
dx: -6cm,
|
||||
dy: 21.3cm,
|
||||
rect(
|
||||
stroke: gray + 2pt,
|
||||
fill: white,
|
||||
radius: 0.5cm,
|
||||
//image("../assets/icon.svg", width: 3cm)
|
||||
image("../assets/icon.svg", width: 4cm)
|
||||
)
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
#show: isc-poster.with(
|
||||
title: meta.title,
|
||||
subtitle: meta.subtitle,
|
||||
student: meta.authors,
|
||||
permanent-email: meta.permanent-email,
|
||||
supervisor:meta.thesis-supervisor,
|
||||
co-supervisor: none,
|
||||
expert: meta.thesis-expert,
|
||||
thesis-id: meta.thesis-id,
|
||||
academic-year: "2025-2026",
|
||||
school: meta.school,
|
||||
programme: "Informatique et systèmes de communication",
|
||||
major: meta.major,
|
||||
orientation: poster-orientation,
|
||||
language: "en", // Valid values are [en, fr, de]
|
||||
num-columns: 2,
|
||||
distribute-columns: true,
|
||||
)
|
||||
|
||||
#let Midas = [_*Midas*_]
|
||||
|
||||
#isc-card(title: "Summary")[
|
||||
Python is one of the most popular programming languages, especially in data science. Although highly flexible and somewhat easy to learn, its leniency can often lead to type errors. Indeed, with duck-typing, the developer is responsible for making sure operations are valid and do what they are meant to do.
|
||||
This project introduces #Midas, a new type system built on top of Python's type hints, capable of checking types at *compile-time* and generating *runtime assertions* for cases that are not statically known.
|
||||
#Midas allows users to define *custom types* in a simple DSL, use them in Python type hints and compile their code to a fully type-checked script with added assertions.
|
||||
Its typing rules are stricter than Python's and allow typing complex *operations on Pandas dataframes*, as well as *dependent types*.
|
||||
]
|
||||
|
||||
#isc-card(title: "Motivations")[
|
||||
In real world data science, values may come in a variety of forms.
|
||||
Sometimes they can be mixed but sometimes they cannot.
|
||||
This is exactly what happened to NASA's Mars Climate Orbiter, which failed its orbital insertion because of a unit mixup@MCOReport.
|
||||
|
||||
While some programming languages use some form of static typing, Python applies the principles of _duck-typing_. This defers attributes and methods checks to *runtime* access, and omits any real type checks.
|
||||
For example, the following:
|
||||
```python
|
||||
price_chf = 3.2
|
||||
price_usd = 9.5
|
||||
total = price_chf + price_usd
|
||||
```
|
||||
|
||||
is perfectly valid Python code that will execute without error and even pass static type checking. However, the result will be a meaningless combination of two float values of different types.
|
||||
|
||||
#Midas was created to help developers and data engineers by (1)~allowing custom type definitions in a more powerful language than Python's type annotations, (2)~strictly carrying out static type checking and (3)~generating assertions to verify at runtime values which cannot be checked statically. This new type system also allows users to define some *dependent types*.
|
||||
]
|
||||
|
||||
#isc-card(title: "Implementation")[
|
||||
The type checker is composed of four main elements, as shown in @fig:architecture:
|
||||
- *Types Registry*: holds all builtin and user-defined types
|
||||
- *Midas Typer*: parses custom definitions and registers types
|
||||
- *Python Typer*: checks Python source code
|
||||
- *Generator*: inserts runtime assertions to produce a runnable script
|
||||
|
||||
#figure(
|
||||
{
|
||||
set par(justify: false)
|
||||
architecture.overview
|
||||
},
|
||||
caption: [Implementation architecture overview]
|
||||
) <fig:architecture>
|
||||
|
||||
First, typing rules were formally defined, drawing heavily on _Types and Progamming Languages_@tapl.
|
||||
The implementation of the parser for the definition language follows R. Nystrom's _Crafting Interpreters_@Nystrom2021. The whole system is itself implemented in Python, leveraging the language's own `ast` module to parse and manipulate source code.
|
||||
The type checker also includes some machinery to infer the result types of several *dataframe operations* and *aggregation methods*.
|
||||
]
|
||||
|
||||
#isc-colbreak()
|
||||
|
||||
#isc-card(title: "Results")[
|
||||
Developers can define *custom types* and *predicates* using the Midas language. *Dependent types* and *dataframe schemas* are supported, as shown in @fig:example-midas.
|
||||
|
||||
#codly(
|
||||
header: [types.midas]
|
||||
)
|
||||
#figure(
|
||||
```midas
|
||||
predicate not_empty(text: str) = len(text) != 0
|
||||
type Height = float where _ >= 0
|
||||
alias People = Frame[
|
||||
name: str where not_empty(_),
|
||||
height: Height
|
||||
]
|
||||
```,
|
||||
caption: [Example custom type definitions]
|
||||
) <fig:example-midas>
|
||||
|
||||
These types can then be used in a regular Python script. Running the type checker and compiler will then produce _diagnostics_. Some may be simple warnings about unknown variables, but definite type errors will prevent compilation. As demonstrated in @fig:example-python, type checking of dataframe operations is rather extensive, providing great coverage of common data science applications.
|
||||
#codly(
|
||||
header: [script.py],
|
||||
highlights: (
|
||||
(
|
||||
line: 3,
|
||||
start: 11,
|
||||
tag: [#h(2pt)Column[Height]],
|
||||
fill: blue.lighten(60%)
|
||||
),
|
||||
(
|
||||
line: 4,
|
||||
start: 17,
|
||||
tag: [#h(2pt)Height],
|
||||
fill: blue.lighten(60%)
|
||||
),
|
||||
)
|
||||
)
|
||||
#figure(
|
||||
```python
|
||||
import pandas as pd
|
||||
people: People = cast(People, pd.read_csv(...))
|
||||
heights = people["height"]
|
||||
median_height = heights.median()
|
||||
```,
|
||||
caption: [Example type checking of dataframe operations]
|
||||
) <fig:example-python>
|
||||
|
||||
Cast expressions such as in @fig:example-python:2 insert runtime assertions to check that values do conform to the expected type.
|
||||
]
|
||||
|
||||
#let repos = (
|
||||
([Main repository], path("figs/qr_gitea.svg"), meta.project-repos),
|
||||
([Mirror], path("figs/qr_github.svg"), meta.mirror-repo),
|
||||
)
|
||||
|
||||
#isc-card(title: "Conclusion")[
|
||||
#Midas provides a solid foundation for a hybrid typing system which can make Python better and safer. Its modular architecture makes it easily extensible.
|
||||
|
||||
*Strengths~:* strict static checking of a great subset of Python, thorough runtime checking of cast expressions and dependent types, extensive type inference of dataframe operations, modular and easily extensible
|
||||
|
||||
*Current Limitations~:* bypass of logical short-circuiting, unsupported reverse operators, oblivious to references and remote modifications
|
||||
|
||||
*Possible Extensions~:* multi-file projects with ```py import``` statements, Numpy arrays, constraint solving
|
||||
|
||||
The source code is published openly under the Apache 2.0 license.
|
||||
|
||||
#grid(
|
||||
columns: (1fr,) * repos.len(),
|
||||
align: center,
|
||||
..repos.map(r => strong(text(size: 0.8em, r.at(0)))),
|
||||
..repos.map(r => image(r.at(1), width: 3cm)),
|
||||
..repos.map(r => text(size: 0.8em, r.at(2)))
|
||||
)
|
||||
]
|
||||
|
||||
#isc-card(title: "References")[
|
||||
#bibliography("../report/bibliography.bib", title: none)
|
||||
]
|
||||
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="164.258pt"
|
||||
height="39.507999pt"
|
||||
viewBox="-2 -2 164.258 39.507998"
|
||||
version="1.1"
|
||||
id="svg41"
|
||||
sodipodi:docname="HES-SO_logo_CMJN.eps"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview43"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="pt"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.0842707"
|
||||
inkscape:cx="99.516243"
|
||||
inkscape:cy="35.642342"
|
||||
inkscape:window-width="5120"
|
||||
inkscape:window-height="2083"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="40"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg41" />
|
||||
<defs
|
||||
id="defs17">
|
||||
<clipPath
|
||||
id="clip-0">
|
||||
<path
|
||||
clip-rule="nonzero"
|
||||
d="m 158,73 h 21 v 27.87109 h -21 z m 0,0"
|
||||
id="path2" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clip-1">
|
||||
<path
|
||||
clip-rule="nonzero"
|
||||
d="m 182,73 h 26.35937 v 27.87109 H 182 Z m 0,0"
|
||||
id="path5" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clip-2">
|
||||
<path
|
||||
clip-rule="nonzero"
|
||||
d="M 49.101562,65.363281 H 82 V 100.87109 H 49.101562 Z m 0,0"
|
||||
id="path8" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clip-3">
|
||||
<path
|
||||
clip-rule="nonzero"
|
||||
d="m 86,73 h 27 v 27.87109 H 86 Z m 0,0"
|
||||
id="path11" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clip-4">
|
||||
<path
|
||||
clip-rule="nonzero"
|
||||
d="m 118,73 h 23 v 27.87109 h -23 z m 0,0"
|
||||
id="path14" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g
|
||||
clip-path="url(#clip-0)"
|
||||
id="g21"
|
||||
transform="translate(-49.101562,-65.363281)">
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
fill="#ab9f93"
|
||||
fill-opacity="1"
|
||||
d="m 171.23828,85.125 c 2.08984,0.640625 6.85156,2.167969 6.85156,7.390625 0,1.96875 -0.78515,4.183594 -2.92968,5.8125 -2.76954,2.214845 -6.48438,2.363285 -8.36719,2.363285 -2.82031,0 -5.17578,-0.49219 -7.16016,-1.234379 -0.26172,-0.09766 -0.52344,-0.195312 -0.78515,-0.292969 l -0.26172,-0.445312 c 0.21093,-1.921875 0.21093,-2.167969 0.3125,-4.136719 l 0.52343,-0.148437 c 0.94141,0.691406 3.24219,2.363281 7.26954,2.363281 4.1289,0 5.64843,-2.164063 5.64843,-3.691406 0,-2.265625 -2.25,-3.003907 -5.4414,-3.988281 l -1.67188,-0.542969 c -2.14453,-0.640625 -6.84765,-2.066407 -6.84765,-7.140625 0,-5.074219 4.70312,-7.585938 10.55859,-7.585938 3.87109,0 6.58594,1.035156 7.53125,1.378906 l 0.26172,0.347657 c -0.21094,1.820312 -0.26172,2.015625 -0.42188,3.890625 l -0.46875,0.195312 c -1.30859,-0.789062 -3.34765,-1.96875 -6.74609,-1.96875 -0.73047,0 -1.46094,0.04687 -2.24609,0.246094 -1.56641,0.390625 -2.875,1.425781 -2.875,2.953125 0,2.117187 2.14062,2.757813 5.38671,3.695313 l 1.87891,0.539062"
|
||||
id="path19" />
|
||||
</g>
|
||||
<g
|
||||
clip-path="url(#clip-1)"
|
||||
id="g25"
|
||||
transform="translate(-49.101562,-65.363281)">
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
fill="#ab9f93"
|
||||
fill-opacity="1"
|
||||
d="m 202.66016,87.097656 c 0.0547,1.03125 0,4.332032 -1.20313,6.550782 -1.51562,2.707031 -4.02344,3.296874 -5.95703,3.296874 -1.05078,0 -2.14844,-0.195312 -3.08984,-0.640624 -4.12891,-1.96875 -4.17969,-6.945313 -4.17969,-8.914063 0,-2.066406 0.16015,-4.921875 1.57031,-6.941406 1.82813,-2.757813 4.75781,-2.859375 5.75,-2.859375 2.92969,0 4.60156,1.28125 5.59375,2.808594 1.30469,2.167968 1.51563,5.464843 1.51563,6.699218 m 5.69921,-0.199218 c -0.10546,-3.445313 -0.78515,-6.894532 -3.1875,-9.503907 -2.30078,-2.460937 -5.80468,-3.546875 -9.35937,-3.546875 -5.23047,0 -8.25781,2.019532 -9.72656,3.449219 -3.5,3.300781 -3.60547,8.125 -3.60547,9.996094 0,1.625 0.0508,6.648437 3.60547,10.046875 1.67187,1.625 4.65234,3.300786 9.35937,3.300786 3.66016,0 6.7461,-0.839849 9.04297,-3.007818 1.73047,-1.621093 2.82422,-3.542968 3.39844,-5.957031 0.36719,-1.476562 0.47265,-3.101562 0.47265,-4.777343"
|
||||
id="path23" />
|
||||
</g>
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
fill="#ab9f93"
|
||||
fill-opacity="1"
|
||||
d="m 103.3125,21.941407 c 0,1.71875 -1.39453,3.113281 -3.11328,3.113281 -1.718752,0 -3.109372,-1.394531 -3.109372,-3.113281 0,-1.71875 1.39062,-3.113282 3.109372,-3.113282 1.71875,0 3.11328,1.394532 3.11328,3.113282"
|
||||
id="path27" />
|
||||
<g
|
||||
clip-path="url(#clip-2)"
|
||||
id="g31"
|
||||
transform="translate(-49.101562,-65.363281)">
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
fill="#1a619a"
|
||||
fill-opacity="1"
|
||||
d="m 80.785156,65.660156 c -0.05469,3.792969 -0.105468,6.105469 -0.105468,9.554688 0,8.273437 0.15625,16.496094 0.574218,24.769531 l -0.261718,0.296875 c -4.445313,-0.0508 -5.488282,0 -9.410157,0.14844 l -0.261719,-0.29688 c 0.261719,-6.94531 0.261719,-8.57031 0.15625,-15.214841 -2.089843,-0.05078 -4.183593,-0.101563 -6.273437,-0.101563 -2.039063,0 -3.972656,0.05078 -6.273437,0.148438 0.05078,7.140625 0.101562,8.914062 0.417968,15.019531 l -0.261718,0.296875 c -4.597657,-0.0508 -5.542969,0 -9.359376,0.14844 l -0.257812,-0.29688 c 0.101562,-4.136716 0.207031,-6.546872 0.207031,-10.980466 0,-12.214844 -0.3125,-18.269532 -0.574219,-23.34375 l 0.3125,-0.296875 c 4.550782,0 5.332032,0 9.359376,-0.148438 l 0.257812,0.296875 -0.101562,13 c 1.832031,0 3.710937,0.05078 5.539062,0.05078 2.355469,0 4.65625,-0.05078 6.957031,-0.09766 C 71.375,72.800781 71.375,71.371094 71.007812,65.808594 l 0.261719,-0.296875 c 4.390625,0 5.070313,0 9.253907,-0.148438 l 0.261718,0.296875"
|
||||
id="path29" />
|
||||
</g>
|
||||
<g
|
||||
clip-path="url(#clip-3)"
|
||||
id="g35"
|
||||
transform="translate(-49.101562,-65.363281)">
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
fill="#1a619a"
|
||||
fill-opacity="1"
|
||||
d="m 95.257812,84.175781 c 0.15625,-1.527343 0.519532,-3.59375 2.507813,-4.777343 0.628906,-0.390626 1.363281,-0.539063 2.089844,-0.539063 1.726561,0 2.769531,0.835937 3.347661,1.722656 0.73046,1.085938 0.78515,2.265625 0.78515,3.398438 z m 15.839848,8.816407 c -1.98828,0.984374 -4.33985,2.117187 -8.15625,2.117187 -3.136722,0 -5.281254,-0.933594 -6.53516,-2.707031 -0.785156,-1.035156 -0.941406,-2.019532 -1.148438,-3.003906 l 16.886718,-0.296876 0.3125,-0.34375 c -0.0508,-2.414062 -0.10547,-4.875 -0.83594,-7.339843 -1.83203,-5.8125 -5.85546,-6.992188 -7.63281,-7.386719 -1.30859,-0.34375 -2.51172,-0.441406 -3.71484,-0.441406 -8.207034,0 -13.644534,5.070312 -13.644534,13.589844 0,2.316406 0.417969,4.726562 1.253906,6.453124 3.035157,6.648438 10.144532,7.238278 13.910158,7.238278 4.33984,0 6.22266,-0.73828 8.99219,-1.773434 l 0.3125,-0.394531 0.36718,-5.5625 -0.36718,-0.148437"
|
||||
id="path33" />
|
||||
</g>
|
||||
<g
|
||||
clip-path="url(#clip-4)"
|
||||
id="g39"
|
||||
transform="translate(-49.101562,-65.363281)">
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
fill="#1a619a"
|
||||
fill-opacity="1"
|
||||
d="m 133.16016,84.175781 c 2.875,1.035157 6.90234,2.464844 6.90234,7.878907 0,0.347656 0,0.691406 -0.0547,1.085937 -0.9375,7.042965 -8.78125,7.730465 -12.7539,7.730465 -3.81641,0 -6.01563,-0.78515 -8.57422,-1.671871 l -0.26172,-0.445313 c 0.21094,-2.460937 0.26172,-3.003906 0.36719,-5.5625 l 0.46875,-0.199218 c 0.78125,0.59375 1.20312,0.9375 2.09375,1.378906 1.92968,0.9375 4.07812,1.378906 5.53906,1.378906 2.92578,0 4.1289,-1.328125 4.1289,-2.558594 0,-1.773437 -2.30078,-2.515625 -3.71093,-2.957031 l -1.98828,-0.691406 c -2.45703,-0.882813 -6.79297,-2.460938 -6.79297,-7.730469 0,-1.96875 0.73047,-3.445312 1.41015,-4.332031 2.71875,-3.695313 7.84375,-3.890625 10.40235,-3.890625 3.39843,0 5.54297,0.589844 8,1.230468 l 0.26172,0.292969 -0.52344,5.371094 -0.46875,0.148437 C 136.29686,79.84375 134.2578,78.5625 131.0703,78.5625 c -3.24218,0 -3.66015,1.625 -3.66015,2.214844 0,1.378906 1.41015,1.921875 3.1875,2.511718 l 2.5625,0.886719"
|
||||
id="path37" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 369.92 127.56">
|
||||
<!-- Generator: Adobe Illustrator 30.1.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 136) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill: #231f20;
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill: #e20571;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st1" d="M204.44,23.01c-15.5,32.84-15.05,31.73-15.49,32.84-.86,1.7-1.73,3.47-3.96,3.47-1.83,0-3.27-1.52-3.27-3.45,0-1.04.22-1.5.95-3.02.49-1.06.04-.07,14.88-31.7,1.02-2.11,2-4.12,4.25-4.12,1.9,0,3.32,1.43,3.32,3.33,0,1.05-.33,1.85-.69,2.63M206.85,20.38c0-2.83-2.22-5.05-5.05-5.05-3.34,0-4.71,2.85-5.72,4.93.46-1-.07.16-14.93,31.78-.8,1.68-1.16,2.41-1.16,3.84,0,2.89,2.19,5.16,4.99,5.16,3.28,0,4.56-2.55,5.5-4.42l15.51-32.88c.43-.93.86-1.96.86-3.36"/>
|
||||
<path class="st1" d="M218.65,23.01c-15.49,32.84-14.93,32.05-15.5,32.84-.85,1.71-1.73,3.47-3.95,3.47-1.84,0-3.27-1.52-3.27-3.45,0-1.04.22-1.5.96-3.02.48-1.07.03-.07,14.88-31.7,1.02-2.1,2-4.12,4.25-4.12,1.89,0,3.34,1.43,3.34,3.33,0,1.05-.33,1.85-.7,2.63M216.02,15.33c-3.34,0-4.71,2.85-5.72,4.93.48-.98-.08.16-14.93,31.78-.8,1.68-1.16,2.41-1.16,3.84,0,2.89,2.2,5.16,4.99,5.16,3.29,0,4.57-2.55,5.5-4.43l15.51-32.87c.42-.89.86-1.96.86-3.36,0-2.83-2.22-5.05-5.05-5.05"/>
|
||||
<path class="st1" d="M230.33,35.02c.28.68.64,1.23,1.58,1.23s1.3-.55,1.58-1.23l4.67-11.87c.1-.28.14-.49.14-.79,0-.89-.78-1.54-1.64-1.54-.72,0-1.17.47-1.45,1.04l-3.31,8.86-3.3-8.86c-.29-.57-.73-1.04-1.46-1.04-.85,0-1.64.64-1.64,1.54,0,.3.04.51.15.79l4.66,11.87Z"/>
|
||||
<path class="st1" d="M243.25,24.14h.04l1.98,6.18h-4.05l2.03-6.18ZM236.56,34.89c0,.85.72,1.49,1.57,1.49s1.31-.47,1.53-1.13l.64-1.98h5.92l.64,2c.21.64.66,1.11,1.56,1.11.79,0,1.47-.57,1.47-1.4,0-.15-.04-.45-.17-.79l-4.18-11.59c-.36-1.02-1.15-1.77-2.28-1.77s-1.91.79-2.28,1.77l-4.29,11.59c-.04.11-.14.36-.14.71"/>
|
||||
<path class="st1" d="M252.98,36.12h5.33c.93,0,1.49-.7,1.54-1.41.02-.72-.45-1.54-1.54-1.54h-3.79v-10.7c0-1.06-.68-1.66-1.6-1.66s-1.6.6-1.6,1.66v11.89c0,1.07.62,1.75,1.66,1.75"/>
|
||||
<path class="st1" d="M267.24,24.14h.05l1.98,6.18h-4.05l2.02-6.18ZM262.12,36.38c.88,0,1.32-.47,1.54-1.13l.64-1.98h5.91l.65,2c.21.64.66,1.11,1.55,1.11.79,0,1.48-.57,1.48-1.4,0-.15-.05-.45-.17-.79l-4.18-11.59c-.37-1.02-1.15-1.77-2.29-1.77s-1.91.79-2.28,1.77l-4.28,11.59c-.05.11-.15.36-.15.71,0,.85.72,1.49,1.57,1.49"/>
|
||||
<path class="st1" d="M278.51,34.59v-12.1c0-1.06-.68-1.66-1.6-1.66s-1.6.6-1.6,1.66v12.1c0,1.07.69,1.66,1.6,1.66s1.6-.59,1.6-1.66"/>
|
||||
<path class="st1" d="M285.54,33.43c-1.38,0-2.57-.83-3.57-.83-.88,0-1.43.68-1.43,1.51,0,2,3.53,2.39,5.18,2.39,2.94,0,5.46-1.9,5.46-4.95,0-5.5-7.12-4.09-7.12-6.29,0-.89.83-1.38,1.75-1.38,1.28,0,1.96.64,2.83.64s1.43-.75,1.43-1.57c0-1.68-2.98-2.13-4.17-2.13-2.84,0-5.29,1.85-5.29,4.83,0,4.94,7.11,3.43,7.11,5.94,0,1.05-.77,1.83-2.18,1.83"/>
|
||||
<path class="st1" d="M242.78,41.53c0-.68-.57-1.41-1.51-1.41-1.02,0-1.51.59-1.7,1.54l-1.81,9.05h-.05l-2.77-9.33c-.23-.77-.88-1.26-1.66-1.26s-1.43.49-1.66,1.26l-2.77,9.33h-.04l-1.82-9.05c-.18-.95-.68-1.54-1.7-1.54-.95,0-1.52.73-1.52,1.41,0,.3.02.45.09.73l2.71,11.63c.21.9.92,1.66,2.11,1.66,1.04,0,1.85-.68,2.11-1.55l2.47-8.21h.05l2.47,8.21c.26.88,1.07,1.55,2.11,1.55,1.2,0,1.9-.76,2.12-1.66l2.7-11.63c.06-.28.08-.43.08-.73"/>
|
||||
<path class="st1" d="M246.87,49.5l2.04-6.18h.03l1.98,6.18h-4.05ZM251.2,41.77c-.37-1.03-1.16-1.77-2.28-1.77s-1.92.79-2.29,1.77l-4.28,11.59c-.04.11-.15.37-.15.7,0,.86.72,1.49,1.57,1.49s1.32-.46,1.54-1.12l.64-1.98h5.92l.64,2c.21.64.66,1.11,1.55,1.11.79,0,1.47-.57,1.47-1.4,0-.15-.04-.45-.17-.79l-4.18-11.59Z"/>
|
||||
<path class="st1" d="M263.96,52.6h-3.79v-10.69c0-1.07-.68-1.66-1.6-1.66s-1.6.59-1.6,1.66v11.89c0,1.07.62,1.75,1.67,1.75h5.32c.93,0,1.49-.7,1.53-1.4.03-.73-.44-1.54-1.53-1.54"/>
|
||||
<path class="st1" d="M273.46,52.6h-3.79v-10.69c0-1.07-.68-1.66-1.6-1.66s-1.59.59-1.59,1.66v11.89c0,1.07.62,1.75,1.66,1.75h5.33c.93,0,1.49-.7,1.53-1.4.02-.73-.45-1.54-1.53-1.54"/>
|
||||
<path class="st1" d="M277.57,40.12c-.91,0-1.6.59-1.6,1.66v12.1c0,1.07.68,1.66,1.6,1.66s1.6-.59,1.6-1.66v-12.1c0-1.07-.68-1.66-1.6-1.66"/>
|
||||
<path class="st1" d="M284.05,44.32c0-.9.83-1.39,1.75-1.39,1.28,0,1.96.64,2.83.64s1.43-.74,1.43-1.57c0-1.69-2.98-2.13-4.18-2.13-2.83,0-5.28,1.85-5.28,4.83,0,4.95,7.11,3.43,7.11,5.95,0,1.04-.77,1.83-2.17,1.83s-2.58-.83-3.58-.83c-.88,0-1.43.68-1.43,1.51,0,2,3.54,2.39,5.18,2.39,2.95,0,5.46-1.89,5.46-4.94,0-5.5-7.12-4.09-7.12-6.29"/>
|
||||
<path class="st1" d="M140.09,40.15l-1.84-.53c-3.18-.91-5.27-1.54-5.27-3.61,0-1.5,1.29-2.51,2.81-2.89.77-.2,1.49-.24,2.2-.24,3.32,0,5.32,1.15,6.6,1.93l.45-.2c.16-1.83.21-2.02.41-3.8l-.25-.34c-.93-.34-3.58-1.35-7.36-1.35-5.73,0-10.32,2.46-10.32,7.42s4.6,6.36,6.7,6.98l1.64.53c3.12.96,5.31,1.68,5.31,3.9,0,1.49-1.48,3.61-5.52,3.61s-6.19-1.64-7.11-2.31l-.51.15c-.1,1.92-.1,2.16-.3,4.04l.25.44c.26.09.52.19.77.29,1.94.73,4.24,1.21,7,1.21,1.84,0,5.47-.14,8.18-2.31,2.09-1.59,2.86-3.75,2.86-5.68,0-5.11-4.65-6.6-6.7-7.23"/>
|
||||
<path class="st1" d="M169.64,48.48c-1.48,2.64-3.93,3.23-5.82,3.23-1.03,0-2.1-.2-3.02-.63-4.04-1.93-4.09-6.79-4.09-8.71,0-2.03.15-4.82,1.54-6.79,1.79-2.69,4.66-2.79,5.62-2.79,2.87,0,4.5,1.25,5.48,2.75,1.27,2.12,1.48,5.35,1.48,6.55.05,1.01,0,4.24-1.18,6.41M164.12,29.12c-5.11,0-8.07,1.97-9.51,3.37-3.43,3.22-3.53,7.94-3.53,9.77,0,1.59.05,6.5,3.53,9.82,1.64,1.6,4.55,3.23,9.15,3.23,3.58,0,6.6-.82,8.84-2.94,1.7-1.59,2.77-3.46,3.32-5.83.36-1.44.46-3.04.46-4.67-.1-3.37-.77-6.74-3.12-9.29-2.25-2.41-5.68-3.46-9.16-3.46"/>
|
||||
<path class="st1" d="M118.64,39.23c-1.69,0-3.04,1.37-3.04,3.04s1.36,3.04,3.04,3.04,3.04-1.36,3.04-3.04-1.36-3.04-3.04-3.04"/>
|
||||
<path class="st1" d="M51.62,21.11l-.25-.29c-4.09.14-4.76.14-9.05.14l-.26.29c.36,5.44.36,6.84.41,12.52-2.25.05-4.5.1-6.8.1-1.79,0-3.63-.05-5.42-.05l.1-12.71-.26-.29c-3.93.14-4.7.14-9.15.14l-.3.29c.26,4.96.57,10.88.57,22.83,0,4.34-.11,6.69-.21,10.74l.25.29c3.73-.14,4.65-.2,9.15-.14l.26-.29c-.31-5.97-.36-7.71-.41-14.69,2.25-.09,4.14-.14,6.14-.14s4.08.05,6.13.1c.1,6.5.1,8.09-.16,14.88l.26.29c3.84-.14,4.86-.2,9.2-.14l.25-.29c-.41-8.09-.56-16.14-.56-24.23,0-3.37.05-5.64.1-9.34"/>
|
||||
<path class="st1" d="M65.78,39.22c.16-1.49.52-3.52,2.46-4.67.61-.39,1.33-.53,2.04-.53,1.69,0,2.7.82,3.27,1.69.71,1.06.76,2.21.76,3.32l-8.54.19ZM74.32,29.3c-1.27-.33-2.45-.43-3.62-.43-8.03,0-13.35,4.96-13.35,13.3,0,2.27.41,4.62,1.22,6.31,2.97,6.5,9.92,7.08,13.61,7.08,4.23,0,6.08-.73,8.79-1.73l.31-.39.36-5.44-.36-.14c-1.95.96-4.25,2.07-7.98,2.07-3.07,0-5.16-.92-6.39-2.66-.77-1-.92-1.96-1.12-2.93l16.52-.29.31-.33c-.05-2.36-.1-4.77-.82-7.18-1.79-5.68-5.72-6.84-7.46-7.23"/>
|
||||
<path class="st1" d="M102.85,39.22l-2.5-.87c-1.74-.58-3.12-1.11-3.12-2.46,0-.58.41-2.17,3.59-2.17s5.11,1.25,6.39,2.03l.46-.15.51-5.25-.25-.29c-2.4-.62-4.5-1.21-7.82-1.21-2.5,0-7.52.2-10.18,3.81-.66.87-1.38,2.31-1.38,4.24,0,5.16,4.25,6.7,6.65,7.56l1.94.67c1.38.44,3.64,1.16,3.64,2.89,0,1.21-1.18,2.51-4.04,2.51-1.43,0-3.53-.43-5.42-1.34-.87-.43-1.29-.78-2.05-1.35l-.46.19c-.1,2.51-.14,3.04-.35,5.45l.25.43c2.5.87,4.65,1.64,8.38,1.64s11.56-.68,12.48-7.56c.05-.39.05-.73.05-1.06,0-5.29-3.93-6.69-6.75-7.7"/>
|
||||
<path class="st2" d="M75,76.07v10.98h-2.09v-4.57h-5.18v4.57h-2.09v-10.98h2.09v4.37h5.18v-4.37h2.09Z"/>
|
||||
<path class="st2" d="M80.95,84.13h-1.36c-.61,0-1,.31-1,.84s.36.78.92.78c.83,0,1.45-.58,1.45-1.62M84.03,86.71c-.29.25-.82.48-1.46.48-.71,0-1.22-.26-1.41-.87-.46.59-1.16.91-2.21.91-1.48,0-2.34-.83-2.34-2.12s.95-2.28,2.94-2.28h1.39v-.44c0-.94-.46-1.31-1.16-1.31s-1.05.32-1.21.9l-1.73-.51c.34-1.24,1.31-2.11,3.1-2.11s2.97.87,2.97,3.07v2.76c0,.2.12.32.3.32.11,0,.19-.04.27-.12l.54,1.31Z"/>
|
||||
<path class="st2" d="M92.07,79.64v7.41h-1.99v-.71c-.51.61-1.26.97-2.26.97-2.01,0-2.84-1.34-2.84-3.3v-4.37h2.04v4.21c0,1,.46,1.66,1.48,1.66.92,0,1.53-.66,1.53-1.75v-4.13h2.04Z"/>
|
||||
<path class="st2" d="M99.6,85.85c-.38.77-1.07,1.41-2.45,1.41-1.56,0-2.5-.83-2.5-2.41l.02-3.52h-1.46v-.44l3.01-2.82h.44v1.58h2.52l-.02,1.68h-2.45v3.37c0,.46.22.8.73.8.41,0,.61-.16.8-.51l1.36.87Z"/>
|
||||
<path class="st2" d="M102.1,82.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M107.37,83.84h-5.32c.15,1.09.8,1.75,1.86,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M119.18,85.23v1.82h-6.44v-10.98h6.27v1.82h-4.18v2.62h3.77v1.84h-3.77v2.89h4.35Z"/>
|
||||
<path class="st2" d="M120.2,83.3c0-2.45,1.8-3.93,3.93-3.93,1.39,0,2.5.51,3.18,1.8l-1.48.98c-.38-.68-.92-1.02-1.7-1.02-1.09,0-1.96.8-1.96,2.16s.87,2.19,1.96,2.19c.9,0,1.39-.34,1.75-1.04l1.46.95c-.68,1.34-1.94,1.83-3.33,1.83-2.14,0-3.81-1.34-3.81-3.94"/>
|
||||
<path class="st2" d="M134.09,83.32c0-1.26-.82-2.21-2.02-2.21s-2.02.97-2.02,2.23.8,2.18,2.02,2.18,2.02-.99,2.02-2.19M128.07,83.32c0-2.16,1.62-3.95,4-3.95,2.5,0,4,1.7,4,3.95s-1.63,3.94-4,3.94c-2.54,0-4-1.7-4-3.94"/>
|
||||
<path class="st2" d="M137.58,75.51h2.06v11.54h-2.06v-11.54Z"/>
|
||||
<path class="st2" d="M143.22,82.45h3.31c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M148.49,83.84h-5.32c.15,1.09.8,1.75,1.85,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M159.03,83.3c0-1.24-.8-2.16-1.99-2.16s-2.04.92-2.04,2.16.8,2.19,2.04,2.19c1.17,0,1.99-.9,1.99-2.19M161,75.51v11.54h-1.96v-.99c-.56.79-1.43,1.19-2.5,1.19-2.06,0-3.54-1.57-3.52-3.96,0-2.33,1.53-3.93,3.54-3.93,1.02,0,1.84.32,2.38,1v-4.86h2.06Z"/>
|
||||
<path class="st2" d="M162.98,75.39h2.24l-1.31,3.8-1.48-.18.55-3.62Z"/>
|
||||
<path class="st2" d="M166.82,76.07h2.09v10.98h-2.09v-10.98Z"/>
|
||||
<path class="st2" d="M178.38,82.65v4.4h-2.04v-4.16c0-1.05-.53-1.75-1.56-1.75s-1.6.76-1.6,1.77v4.15h-2.06v-7.41h1.91v.83c.51-.7,1.29-1.11,2.43-1.11,1.95,0,2.93,1.32,2.93,3.28"/>
|
||||
<path class="st2" d="M182.93,83.72c.71,0,1.19-.56,1.19-1.26s-.46-1.22-1.19-1.22c-.78,0-1.22.53-1.22,1.22,0,.73.46,1.26,1.22,1.26M186.96,81.22h-1.34c.25.41.41.87.41,1.34,0,.82-.39,1.5-1.19,2.12l-.88.75c-.15.12-.29.22-.46.29h.92c1.63,0,2.48.8,2.48,2.14,0,1.55-1.39,2.62-3.77,2.62-1.8,0-2.98-.51-3.59-1.67l1.43-1.09c.41.75,1,1.12,2.14,1.12,1.27,0,1.9-.29,1.9-.88,0-.46-.3-.66-.8-.66h-2.79v-1.24l1.05-.9c-1.63-.15-2.68-1.14-2.68-2.7s1.29-2.82,3.14-2.82h4.05l-.02,1.58Z"/>
|
||||
<path class="st2" d="M191.71,78.79l-1.52-.38,1.28-2.99h2.55l-2.31,3.37ZM189.75,82.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M195.02,83.84h-5.32c.15,1.09.8,1.75,1.85,1.75.85,0,1.34-.32,1.68-.99l1.62.7c-.58,1.27-1.79,1.95-3.35,1.95-2.12,0-3.8-1.28-3.8-3.93,0-2.19,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.81c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M203.84,82.65v4.4h-2.04v-4.16c0-1.05-.53-1.75-1.57-1.75s-1.6.76-1.6,1.77v4.15h-2.05v-7.41h1.9v.83c.51-.7,1.29-1.11,2.43-1.11,1.96,0,2.93,1.32,2.93,3.28"/>
|
||||
<path class="st2" d="M205.77,79.64h2.05v7.41h-2.05v-7.41ZM205.44,77.08c0-.75.59-1.34,1.36-1.34s1.36.59,1.36,1.34-.61,1.38-1.36,1.38-1.36-.61-1.36-1.38"/>
|
||||
<path class="st2" d="M211.41,82.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M216.68,83.84h-5.32c.16,1.09.8,1.75,1.86,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M222.59,79.46v2.02c-1.31,0-2.29.8-2.29,2.25v3.33h-2.06v-7.41h2.01v1.12c.47-.8,1.22-1.31,2.34-1.31"/>
|
||||
<path class="st2" d="M223.87,79.64h2.05v7.41h-2.05v-7.41ZM223.55,77.08c0-.75.59-1.34,1.36-1.34s1.36.59,1.36,1.34-.61,1.38-1.36,1.38-1.36-.61-1.36-1.38"/>
|
||||
<path class="st2" d="M229.51,82.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M234.78,83.84h-5.32c.15,1.09.8,1.75,1.86,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M75,96.07v10.98h-2.09v-4.57h-5.18v4.57h-2.09v-10.98h2.09v4.37h5.18v-4.37h2.09Z"/>
|
||||
<path class="st2" d="M82.62,103.32c0-1.26-.82-2.21-2.02-2.21s-2.02.97-2.02,2.23.8,2.18,2.02,2.18,2.02-.99,2.02-2.19M76.6,103.32c0-2.16,1.62-3.95,4-3.95,2.5,0,4,1.7,4,3.95s-1.63,3.94-4,3.94c-2.53,0-4-1.7-4-3.94"/>
|
||||
<path class="st2" d="M85.61,103.3c0-2.45,1.8-3.93,3.93-3.93,1.39,0,2.5.51,3.18,1.8l-1.48.98c-.37-.68-.92-1.02-1.7-1.02-1.09,0-1.95.8-1.95,2.16s.87,2.19,1.95,2.19c.9,0,1.39-.34,1.75-1.04l1.46.95c-.68,1.34-1.94,1.83-3.33,1.83-2.14,0-3.81-1.34-3.81-3.94"/>
|
||||
<path class="st2" d="M101.28,102.69v4.37h-2.04v-4.18c0-1-.54-1.73-1.56-1.73s-1.58.76-1.58,1.75v4.16h-2.05v-11.54h2.05v4.05l-.04.78c.5-.61,1.24-.97,2.3-.97,1.95,0,2.92,1.36,2.92,3.32"/>
|
||||
<path class="st2" d="M102.59,105.23l1.6-.59c.25.77.7,1.04,1.48,1.04.7,0,1.09-.25,1.09-.73s-.27-.66-1.51-.92c-1.62-.34-2.35-1.05-2.35-2.31,0-1.44,1.16-2.34,2.74-2.34,1.46,0,2.34.53,2.7,1.77l-1.62.59c-.2-.51-.48-.8-1.11-.8-.54,0-.87.27-.87.65,0,.42.27.63,1.53.88,1.7.36,2.33,1.07,2.33,2.41-.02,1.45-1.17,2.38-2.99,2.38-1.66,0-2.67-.63-3.02-2.02"/>
|
||||
<path class="st2" d="M109.63,103.3c0-2.45,1.8-3.93,3.93-3.93,1.39,0,2.5.51,3.18,1.8l-1.48.98c-.38-.68-.92-1.02-1.7-1.02-1.09,0-1.96.8-1.96,2.16s.87,2.19,1.96,2.19c.9,0,1.39-.34,1.75-1.04l1.46.95c-.68,1.34-1.94,1.83-3.33,1.83-2.14,0-3.81-1.34-3.81-3.94"/>
|
||||
<path class="st2" d="M125.3,102.69v4.37h-2.04v-4.18c0-1-.54-1.73-1.56-1.73s-1.58.76-1.58,1.75v4.16h-2.05v-11.54h2.05v4.05l-.03.78c.49-.61,1.24-.97,2.29-.97,1.96,0,2.93,1.36,2.93,3.32"/>
|
||||
<path class="st2" d="M134.21,99.64v7.41h-1.99v-.71c-.51.61-1.26.97-2.26.97-2,0-2.84-1.34-2.84-3.3v-4.37h2.04v4.21c0,1,.46,1.66,1.48,1.66.92,0,1.53-.66,1.53-1.75v-4.13h2.04Z"/>
|
||||
<path class="st2" d="M136.25,95.51h2.05v11.54h-2.05v-11.54Z"/>
|
||||
<path class="st2" d="M141.89,102.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M147.16,103.84h-5.32c.15,1.09.8,1.75,1.86,1.75.85,0,1.34-.32,1.68-.99l1.62.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M154.85,99.64h1.94v1.68h-1.94v5.73h-2.05v-5.73h-1.16v-1.68h1.16v-1.39c0-1.8,1.12-2.88,2.82-2.88,1.04,0,1.82.38,2.33,1.16l-1.32,1.16c-.17-.32-.44-.49-.83-.49-.6,0-.94.39-.94,1.14v1.31Z"/>
|
||||
<path class="st2" d="M162.02,97.09c0-.71.58-1.29,1.29-1.29s1.27.58,1.27,1.29-.58,1.27-1.27,1.27-1.29-.58-1.29-1.27M158.4,97.09c.02-.71.59-1.29,1.29-1.29s1.29.58,1.29,1.29-.58,1.27-1.29,1.27-1.28-.58-1.29-1.27M164.96,99.64v7.41h-1.99v-.71c-.51.61-1.26.97-2.26.97-2.01,0-2.84-1.34-2.84-3.3v-4.37h2.04v4.21c0,1,.46,1.66,1.48,1.66.92,0,1.53-.66,1.53-1.75v-4.13h2.04Z"/>
|
||||
<path class="st2" d="M171.36,99.46v2.02c-1.31,0-2.29.8-2.29,2.25v3.33h-2.06v-7.41h2.01v1.12c.47-.8,1.22-1.31,2.34-1.31"/>
|
||||
<path class="st2" d="M176.12,96.07h2.09v10.98h-2.09v-10.98Z"/>
|
||||
<path class="st2" d="M187.68,102.65v4.4h-2.04v-4.16c0-1.05-.53-1.75-1.57-1.75s-1.6.76-1.6,1.77v4.15h-2.05v-7.41h1.9v.83c.51-.7,1.29-1.11,2.43-1.11,1.96,0,2.93,1.32,2.93,3.28"/>
|
||||
<path class="st2" d="M192.23,103.72c.71,0,1.19-.56,1.19-1.26s-.46-1.22-1.19-1.22c-.78,0-1.22.53-1.22,1.22,0,.73.46,1.26,1.22,1.26M196.26,101.22h-1.34c.25.41.41.87.41,1.34,0,.82-.39,1.5-1.19,2.12l-.89.75c-.15.12-.29.22-.46.29h.92c1.63,0,2.48.8,2.48,2.14,0,1.55-1.39,2.62-3.77,2.62-1.8,0-2.97-.51-3.59-1.67l1.43-1.09c.41.75,1,1.12,2.14,1.12,1.27,0,1.9-.29,1.9-.88,0-.46-.3-.66-.8-.66h-2.79v-1.24l1.05-.9c-1.63-.15-2.69-1.14-2.69-2.7s1.29-2.82,3.14-2.82h4.05l-.02,1.58Z"/>
|
||||
<path class="st2" d="M199.05,102.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M204.32,103.84h-5.32c.16,1.09.8,1.75,1.86,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M213.14,102.65v4.4h-2.04v-4.16c0-1.05-.53-1.75-1.57-1.75s-1.59.76-1.59,1.77v4.15h-2.06v-7.41h1.9v.83c.51-.7,1.29-1.11,2.43-1.11,1.95,0,2.92,1.32,2.92,3.28"/>
|
||||
<path class="st2" d="M215.06,99.64h2.06v7.41h-2.06v-7.41ZM214.74,97.08c0-.75.6-1.34,1.36-1.34s1.36.59,1.36,1.34-.61,1.38-1.36,1.38-1.36-.61-1.36-1.38"/>
|
||||
<path class="st2" d="M220.71,102.45h3.32c-.16-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M225.98,103.84h-5.32c.15,1.09.8,1.75,1.85,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.78,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.52-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M234.53,99.64v7.41h-1.99v-.71c-.51.61-1.26.97-2.26.97-2.01,0-2.84-1.34-2.84-3.3v-4.37h2.04v4.21c0,1,.46,1.66,1.48,1.66.92,0,1.53-.66,1.53-1.75v-4.13h2.04Z"/>
|
||||
<path class="st2" d="M240.92,99.46v2.02c-1.31,0-2.29.8-2.29,2.25v3.33h-2.06v-7.41h2.01v1.12c.48-.8,1.22-1.31,2.34-1.31"/>
|
||||
<path class="st2" d="M253.02,99.64l-3.13,7.61h-.88l-1.85-4.1-1.94,4.1h-.9l-3.11-7.61h2.2l1.58,4.4,2.04-4.5h.32l1.99,4.5,1.63-4.4h2.06Z"/>
|
||||
<path class="st2" d="M254.04,99.64h2.06v7.41h-2.06v-7.41ZM253.72,97.08c0-.75.59-1.34,1.36-1.34s1.36.59,1.36,1.34-.61,1.38-1.36,1.38-1.36-.61-1.36-1.38"/>
|
||||
<path class="st2" d="M257.51,105.23l1.6-.59c.25.77.7,1.04,1.48,1.04.7,0,1.09-.25,1.09-.73s-.27-.66-1.51-.92c-1.62-.34-2.35-1.05-2.35-2.31,0-1.44,1.16-2.34,2.74-2.34,1.46,0,2.34.53,2.7,1.77l-1.62.59c-.2-.51-.47-.8-1.1-.8-.55,0-.87.27-.87.65,0,.42.27.63,1.53.88,1.7.36,2.33,1.07,2.33,2.41-.02,1.45-1.17,2.38-2.99,2.38-1.67,0-2.67-.63-3.03-2.02"/>
|
||||
<path class="st2" d="M264.43,105.23l1.6-.59c.25.77.7,1.04,1.48,1.04.7,0,1.09-.25,1.09-.73s-.27-.66-1.51-.92c-1.61-.34-2.35-1.05-2.35-2.31,0-1.44,1.16-2.34,2.74-2.34,1.46,0,2.35.53,2.7,1.77l-1.61.59c-.21-.51-.48-.8-1.11-.8-.54,0-.87.27-.87.65,0,.42.27.63,1.53.88,1.7.36,2.33,1.07,2.33,2.41-.02,1.45-1.18,2.38-2.99,2.38-1.67,0-2.67-.63-3.03-2.02"/>
|
||||
<path class="st2" d="M273.52,102.45h3.32c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M278.79,103.84h-5.32c.15,1.09.8,1.75,1.86,1.75.85,0,1.34-.32,1.68-.99l1.61.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.51-3.96,3.82-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M287.62,102.65v4.4h-2.04v-4.16c0-1.05-.53-1.75-1.57-1.75s-1.59.76-1.59,1.77v4.15h-2.06v-7.41h1.9v.83c.51-.7,1.29-1.11,2.43-1.11,1.95,0,2.92,1.32,2.92,3.28"/>
|
||||
<path class="st2" d="M288.93,105.23l1.6-.59c.26.77.7,1.04,1.48,1.04.7,0,1.09-.25,1.09-.73s-.27-.66-1.52-.92c-1.61-.34-2.34-1.05-2.34-2.31,0-1.44,1.16-2.34,2.73-2.34,1.46,0,2.35.53,2.7,1.77l-1.61.59c-.2-.51-.48-.8-1.11-.8-.54,0-.87.27-.87.65,0,.42.27.63,1.53.88,1.7.36,2.33,1.07,2.33,2.41-.02,1.45-1.17,2.38-2.99,2.38-1.67,0-2.67-.63-3.03-2.02"/>
|
||||
<path class="st2" d="M295.96,103.3c0-2.45,1.8-3.93,3.93-3.93,1.39,0,2.5.51,3.18,1.8l-1.48.98c-.37-.68-.92-1.02-1.7-1.02-1.09,0-1.95.8-1.95,2.16s.87,2.19,1.95,2.19c.9,0,1.39-.34,1.75-1.04l1.46.95c-.68,1.34-1.94,1.83-3.33,1.83-2.14,0-3.81-1.34-3.81-3.94"/>
|
||||
<path class="st2" d="M311.64,102.69v4.37h-2.04v-4.18c0-1-.54-1.73-1.56-1.73s-1.58.76-1.58,1.75v4.16h-2.05v-11.54h2.05v4.05l-.04.78c.49-.61,1.24-.97,2.3-.97,1.95,0,2.92,1.36,2.92,3.32"/>
|
||||
<path class="st2" d="M317.37,104.13h-1.36c-.61,0-1,.31-1,.84s.36.78.92.78c.83,0,1.45-.58,1.45-1.62M320.44,106.71c-.29.25-.81.48-1.46.48-.71,0-1.22-.26-1.41-.87-.46.59-1.16.91-2.21.91-1.48,0-2.34-.83-2.34-2.12s.95-2.28,2.94-2.28h1.39v-.44c0-.94-.46-1.31-1.15-1.31s-1.05.32-1.21.9l-1.73-.51c.34-1.24,1.31-2.11,3.09-2.11s2.97.87,2.97,3.07v2.76c0,.2.12.32.31.32.1,0,.18-.04.27-.12l.54,1.31Z"/>
|
||||
<path class="st2" d="M324.08,99.64h1.94v1.68h-1.94v5.73h-2.06v-5.73h-1.16v-1.68h1.16v-1.39c0-1.8,1.12-2.88,2.82-2.88,1.04,0,1.82.38,2.33,1.16l-1.32,1.16c-.17-.32-.45-.49-.83-.49-.6,0-.94.39-.94,1.14v1.31Z"/>
|
||||
<path class="st2" d="M332.7,105.85c-.37.77-1.07,1.41-2.45,1.41-1.57,0-2.5-.83-2.5-2.41l.02-3.52h-1.46v-.44l3.01-2.82h.45v1.58h2.51l-.02,1.68h-2.45v3.37c0,.46.22.8.73.8.41,0,.61-.16.8-.51l1.36.87Z"/>
|
||||
<path class="st2" d="M335.2,102.45h3.31c-.15-.94-.68-1.48-1.58-1.48-.85,0-1.5.49-1.73,1.48M340.47,103.84h-5.32c.15,1.09.8,1.75,1.85,1.75.85,0,1.34-.32,1.68-.99l1.62.7c-.58,1.28-1.79,1.96-3.35,1.96-2.12,0-3.81-1.27-3.81-3.93,0-2.2,1.52-3.96,3.83-3.96s3.55,1.55,3.55,3.8c0,.24,0,.46-.05.66"/>
|
||||
<path class="st2" d="M349.29,102.65v4.4h-2.04v-4.16c0-1.05-.53-1.75-1.57-1.75s-1.6.76-1.6,1.77v4.15h-2.05v-7.41h1.9v.83c.51-.7,1.29-1.11,2.43-1.11,1.96,0,2.93,1.32,2.93,3.28"/>
|
||||
<path class="st2" d="M20.54,104.79c0,1.3,1.06,2.37,2.36,2.37h26.23c1.3,0,2.37-1.06,2.37-2.37v-26.23c0-1.31-1.06-2.37-2.37-2.37h-26.23c-1.3,0-2.36,1.06-2.36,2.37v26.23Z"/>
|
||||
<path class="st0" d="M37.73,86.04h-3.57c-.18,2.07-.35,3.84-.51,5.16-.17,1.32-.32,2.33-.48,3.29-.16.95-.32,1.82-.48,2.6-.14.61-.33,1.09-.59,1.43-.26.35-.64.59-1.14.72-.42.11-.83.02-1.24-.25-.41-.27-.65-.59-.73-.96-.06-.27-.08-.51-.04-.7.02-.19.08-.37.17-.53.08-.17.18-.32.28-.45.1-.13.21-.27.32-.4.04-.04.22-.25.54-.63.32-.38.57-.73.73-1.05.16-.31.52-1,.7-1.86s.55-3.8.54-6.36h-1.5c-.43,0-.84.14-1.23.34-.38.2-.71.54-.87.73-.16.2-.57.86-.67.99-.09.13-.29.42-.44.38-.16-.05-.12-.23-.1-.38.03-.14.32-.89.54-1.44.23-.56.52-1.11.91-1.63.29-.45.7-.76,1.2-1.08.51-.33,1.09-.49,1.77-.49h10.95c.19,0,1.22.08,1.22,1.28s-1.09,1.3-1.22,1.3h-3.11c-.26,2.46-.39,5.01-.39,7.5,0,.45.08.9.23,1.33.16.43.34.77.57.99.29.27.64.44,1.06.52.42.08.85-.05,1.28-.39.24-.19.41-.4.52-.63.11-.24.18-.47.21-.68.03-.22.08-.52.09-.6s0-.34.3-.37.34.25.37.55c.02.29-.13,1.39-.38,2.08-.25.69-.55,1.24-.89,1.65-.34.42-.68.72-1,.91-.38.23-.81.36-1.3.41-.5.05-.96-.07-1.37-.35-.68-.5-1.2-1.27-1.55-2.33-.2-.62-.31-1.44-.33-2.45-.03-1.02,0-2.01.1-2.98l.54-5.15Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,988 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="482.222" height="84.6667" viewBox="0 0 482.222 84.6667">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 6.6875 0 L 1.8125 0 L 1.8125 -18.765625 L 6.6875 -18.765625 Z M 6.6875 0 "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 5.96875 -13.640625 L 5.96875 -12.375 C 6.28125 -12.84375 6.796875 -13.21875 7.515625 -13.5 C 8.234375 -13.78125 8.976562 -13.921875 9.75 -13.921875 C 11.613281 -13.921875 12.992188 -13.394531 13.890625 -12.34375 C 14.785156 -11.300781 15.234375 -9.847656 15.234375 -7.984375 L 15.234375 0 L 10.53125 0 L 10.53125 -6.890625 C 10.53125 -7.972656 10.359375 -8.742188 10.015625 -9.203125 C 9.679688 -9.660156 9.066406 -9.890625 8.171875 -9.890625 C 7.503906 -9.890625 6.863281 -9.71875 6.25 -9.375 L 6.25 0 L 1.546875 0 L 1.546875 -13.640625 Z M 5.96875 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 2.796875 -13.640625 L 2.796875 -14.28125 C 2.796875 -16 3.335938 -17.3125 4.421875 -18.21875 C 5.503906 -19.132812 6.894531 -19.59375 8.59375 -19.59375 C 9.5625 -19.59375 10.410156 -19.519531 11.140625 -19.375 L 11.140625 -15.453125 C 10.640625 -15.546875 10.164062 -15.59375 9.71875 -15.59375 C 8.238281 -15.59375 7.5 -14.941406 7.5 -13.640625 L 11.140625 -13.640625 L 11.140625 -9.859375 L 7.5 -9.859375 L 7.5 0 L 2.796875 0 L 2.796875 -9.859375 L 0.84375 -9.859375 L 0.84375 -13.640625 Z M 2.796875 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<path d="M 0.984375 -6.828125 C 0.984375 -8.847656 1.710938 -10.535156 3.171875 -11.890625 C 4.640625 -13.242188 6.457031 -13.921875 8.625 -13.921875 C 10.789062 -13.921875 12.613281 -13.242188 14.09375 -11.890625 C 15.582031 -10.535156 16.328125 -8.847656 16.328125 -6.828125 C 16.328125 -4.816406 15.585938 -3.128906 14.109375 -1.765625 C 12.617188 -0.398438 10.789062 0.28125 8.625 0.28125 C 6.457031 0.28125 4.640625 -0.398438 3.171875 -1.765625 C 1.710938 -3.128906 0.984375 -4.816406 0.984375 -6.828125 Z M 6.53125 -4.640625 C 7.050781 -4.066406 7.75 -3.78125 8.625 -3.78125 C 9.5 -3.78125 10.207031 -4.0625 10.75 -4.625 C 11.289062 -5.195312 11.5625 -5.929688 11.5625 -6.828125 C 11.5625 -7.703125 11.289062 -8.429688 10.75 -9.015625 C 10.207031 -9.597656 9.5 -9.890625 8.625 -9.890625 C 7.769531 -9.890625 7.070312 -9.597656 6.53125 -9.015625 C 6 -8.429688 5.734375 -7.703125 5.734375 -6.828125 C 5.734375 -5.929688 6 -5.203125 6.53125 -4.640625 Z M 6.53125 -4.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 1.546875 -13.640625 L 5.96875 -13.640625 L 5.96875 -12.234375 C 6.25 -12.734375 6.6875 -13.113281 7.28125 -13.375 C 7.875 -13.644531 8.507812 -13.78125 9.1875 -13.78125 C 9.65625 -13.78125 10.140625 -13.722656 10.640625 -13.609375 L 10.640625 -9.375 C 10.046875 -9.4375 9.457031 -9.46875 8.875 -9.46875 C 7.8125 -9.46875 6.9375 -9.328125 6.25 -9.046875 L 6.25 0 L 1.546875 0 Z M 1.546875 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 5.96875 -13.640625 L 5.96875 -12.375 C 6.28125 -12.84375 6.796875 -13.21875 7.515625 -13.5 C 8.234375 -13.78125 8.976562 -13.921875 9.75 -13.921875 C 11.707031 -13.921875 13.148438 -13.320312 14.078125 -12.125 C 14.421875 -12.644531 14.988281 -13.070312 15.78125 -13.40625 C 16.570312 -13.75 17.367188 -13.921875 18.171875 -13.921875 C 20.078125 -13.921875 21.5625 -13.382812 22.625 -12.3125 C 23.6875 -11.25 24.21875 -9.804688 24.21875 -7.984375 L 24.21875 0 L 19.515625 0 L 19.515625 -6.890625 C 19.515625 -7.972656 19.347656 -8.742188 19.015625 -9.203125 C 18.679688 -9.660156 18.066406 -9.890625 17.171875 -9.890625 C 16.472656 -9.890625 15.796875 -9.710938 15.140625 -9.359375 C 15.203125 -8.921875 15.234375 -8.460938 15.234375 -7.984375 L 15.234375 0 L 10.53125 0 L 10.53125 -6.890625 C 10.53125 -7.972656 10.359375 -8.742188 10.015625 -9.203125 C 9.679688 -9.660156 9.066406 -9.890625 8.171875 -9.890625 C 7.503906 -9.890625 6.863281 -9.71875 6.25 -9.375 L 6.25 0 L 1.546875 0 L 1.546875 -13.640625 Z M 5.96875 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 2.828125 -11.90625 C 4.054688 -13.25 5.671875 -13.921875 7.671875 -13.921875 C 9.410156 -13.921875 10.625 -13.472656 11.3125 -12.578125 L 11.3125 -13.640625 L 15.734375 -13.640625 L 15.734375 0 L 11.3125 0 L 11.3125 -1.0625 C 10.601562 -0.164062 9.390625 0.28125 7.671875 0.28125 C 5.671875 0.28125 4.054688 -0.394531 2.828125 -1.75 C 1.597656 -3.101562 0.984375 -4.796875 0.984375 -6.828125 C 0.984375 -8.847656 1.597656 -10.539062 2.828125 -11.90625 Z M 6.546875 -4.625 C 7.085938 -4.039062 7.789062 -3.75 8.65625 -3.75 C 9.457031 -3.75 10.269531 -3.992188 11.09375 -4.484375 L 11.09375 -9.1875 C 10.34375 -9.65625 9.53125 -9.890625 8.65625 -9.890625 C 7.789062 -9.890625 7.085938 -9.597656 6.546875 -9.015625 C 6.003906 -8.429688 5.734375 -7.703125 5.734375 -6.828125 C 5.734375 -5.929688 6.003906 -5.195312 6.546875 -4.625 Z M 6.546875 -4.625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 2.796875 -13.640625 L 2.796875 -17.046875 L 7.5 -17.046875 L 7.5 -13.640625 L 11.140625 -13.640625 L 11.140625 -9.859375 L 7.5 -9.859375 L 7.5 -6.078125 C 7.5 -5.273438 7.6875 -4.703125 8.0625 -4.359375 C 8.4375 -4.023438 8.988281 -3.859375 9.71875 -3.859375 C 10.164062 -3.859375 10.640625 -3.90625 11.140625 -4 L 11.140625 -0.078125 C 10.410156 0.0664062 9.5625 0.140625 8.59375 0.140625 C 6.851562 0.140625 5.453125 -0.273438 4.390625 -1.109375 C 3.328125 -1.941406 2.796875 -3.207031 2.796875 -4.90625 L 2.796875 -9.859375 L 0.84375 -9.859375 L 0.84375 -13.640625 Z M 2.796875 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 1.546875 0 L 1.546875 -13.640625 L 6.25 -13.640625 L 6.25 0 Z M 1.203125 -17.78125 C 1.203125 -18.488281 1.460938 -19.097656 1.984375 -19.609375 C 2.503906 -20.128906 3.140625 -20.390625 3.890625 -20.390625 C 4.640625 -20.390625 5.273438 -20.128906 5.796875 -19.609375 C 6.316406 -19.097656 6.578125 -18.488281 6.578125 -17.78125 C 6.578125 -17.070312 6.316406 -16.460938 5.796875 -15.953125 C 5.273438 -15.429688 4.640625 -15.171875 3.890625 -15.171875 C 3.140625 -15.171875 2.503906 -15.425781 1.984375 -15.9375 C 1.460938 -16.457031 1.203125 -17.070312 1.203125 -17.78125 Z M 1.203125 -17.78125 "/>
|
||||
</g>
|
||||
<g id="glyph-0-9">
|
||||
<path d="M 2.828125 -11.90625 C 4.054688 -13.25 5.671875 -13.921875 7.671875 -13.921875 C 9.390625 -13.921875 10.601562 -13.472656 11.3125 -12.578125 L 11.3125 -13.640625 L 15.734375 -13.640625 L 15.734375 5.59375 L 11.03125 5.59375 L 11.03125 -0.75 C 10.25 -0.0625 9.128906 0.28125 7.671875 0.28125 C 5.671875 0.28125 4.054688 -0.394531 2.828125 -1.75 C 1.597656 -3.101562 0.984375 -4.785156 0.984375 -6.796875 C 0.984375 -8.835938 1.597656 -10.539062 2.828125 -11.90625 Z M 6.546875 -4.625 C 7.085938 -4.039062 7.789062 -3.75 8.65625 -3.75 C 9.53125 -3.75 10.34375 -3.984375 11.09375 -4.453125 L 11.09375 -9.15625 C 10.269531 -9.644531 9.457031 -9.890625 8.65625 -9.890625 C 7.789062 -9.890625 7.085938 -9.597656 6.546875 -9.015625 C 6.003906 -8.429688 5.734375 -7.691406 5.734375 -6.796875 C 5.734375 -5.921875 6.003906 -5.195312 6.546875 -4.625 Z M 6.546875 -4.625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-10">
|
||||
<path d="M 1.40625 -13.640625 L 6.109375 -13.640625 L 6.109375 -6.1875 C 6.109375 -5.382812 6.28125 -4.773438 6.625 -4.359375 C 6.96875 -3.953125 7.578125 -3.75 8.453125 -3.75 C 9.109375 -3.75 9.71875 -3.898438 10.28125 -4.203125 L 10.28125 -13.640625 L 14.984375 -13.640625 L 14.984375 0 L 10.578125 0 L 10.578125 -1.140625 C 10.242188 -0.710938 9.722656 -0.367188 9.015625 -0.109375 C 8.304688 0.148438 7.597656 0.28125 6.890625 0.28125 C 5.023438 0.28125 3.644531 -0.238281 2.75 -1.28125 C 1.851562 -2.332031 1.40625 -3.789062 1.40625 -5.65625 Z M 1.40625 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-11">
|
||||
<path d="M 15.625 -6.796875 C 15.625 -6.347656 15.597656 -5.898438 15.546875 -5.453125 L 5.59375 -5.453125 C 6.175781 -4.191406 7.53125 -3.5625 9.65625 -3.5625 C 11.132812 -3.5625 12.609375 -3.820312 14.078125 -4.34375 L 14.078125 -0.59375 C 12.703125 -0.0078125 11.085938 0.28125 9.234375 0.28125 C 6.703125 0.28125 4.691406 -0.359375 3.203125 -1.640625 C 1.722656 -2.929688 0.984375 -4.660156 0.984375 -6.828125 C 0.984375 -8.867188 1.675781 -10.5625 3.0625 -11.90625 C 4.457031 -13.25 6.207031 -13.921875 8.3125 -13.921875 C 10.425781 -13.921875 12.171875 -13.25 13.546875 -11.90625 C 14.929688 -10.5625 15.625 -8.859375 15.625 -6.796875 Z M 8.3125 -10.109375 C 6.875 -10.109375 5.941406 -9.472656 5.515625 -8.203125 L 11.03125 -8.203125 C 10.625 -9.472656 9.71875 -10.109375 8.3125 -10.109375 Z M 8.3125 -10.109375 "/>
|
||||
</g>
|
||||
<g id="glyph-0-12">
|
||||
</g>
|
||||
<g id="glyph-0-13">
|
||||
<path d="M 2.4375 -12.65625 C 3.40625 -13.5 4.75 -13.921875 6.46875 -13.921875 C 8.28125 -13.921875 9.894531 -13.65625 11.3125 -13.125 L 11.3125 -9.484375 C 9.632812 -10.015625 8.046875 -10.28125 6.546875 -10.28125 C 5.671875 -10.28125 5.234375 -10.070312 5.234375 -9.65625 C 5.234375 -9.46875 5.34375 -9.320312 5.5625 -9.21875 C 5.789062 -9.125 6.203125 -9.007812 6.796875 -8.875 L 7.984375 -8.625 C 9.472656 -8.300781 10.5625 -7.816406 11.25 -7.171875 C 11.945312 -6.535156 12.296875 -5.617188 12.296875 -4.421875 C 12.296875 -3.023438 11.769531 -1.890625 10.71875 -1.015625 C 9.675781 -0.148438 8.164062 0.28125 6.1875 0.28125 C 4.4375 0.28125 2.820312 -0.0234375 1.34375 -0.640625 L 1.34375 -4.484375 C 3.039062 -3.773438 4.722656 -3.421875 6.390625 -3.421875 C 7.472656 -3.421875 8.015625 -3.660156 8.015625 -4.140625 C 8.015625 -4.347656 7.910156 -4.503906 7.703125 -4.609375 C 7.492188 -4.710938 7.09375 -4.816406 6.5 -4.921875 L 5.1875 -5.203125 C 2.382812 -5.804688 0.984375 -7.191406 0.984375 -9.359375 C 0.984375 -10.710938 1.46875 -11.8125 2.4375 -12.65625 Z M 2.4375 -12.65625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-14">
|
||||
<path d="M 0.140625 -13.640625 L 5.234375 -13.640625 L 8.203125 -6.828125 L 11.203125 -13.640625 L 16.21875 -13.640625 L 7.03125 5.59375 L 2.078125 5.59375 L 5.796875 -1.875 Z M 0.140625 -13.640625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-15">
|
||||
<path d="M 9.21875 -19.40625 L 10.046875 -15.171875 L 6.546875 -15.171875 L 4.3125 -19.40625 Z M 15.625 -6.796875 C 15.625 -6.347656 15.597656 -5.898438 15.546875 -5.453125 L 5.59375 -5.453125 C 6.175781 -4.191406 7.53125 -3.5625 9.65625 -3.5625 C 11.132812 -3.5625 12.609375 -3.820312 14.078125 -4.34375 L 14.078125 -0.59375 C 12.703125 -0.0078125 11.085938 0.28125 9.234375 0.28125 C 6.703125 0.28125 4.691406 -0.359375 3.203125 -1.640625 C 1.722656 -2.929688 0.984375 -4.660156 0.984375 -6.828125 C 0.984375 -8.867188 1.675781 -10.5625 3.0625 -11.90625 C 4.457031 -13.25 6.207031 -13.921875 8.3125 -13.921875 C 10.425781 -13.921875 12.171875 -13.25 13.546875 -11.90625 C 14.929688 -10.5625 15.625 -8.859375 15.625 -6.796875 Z M 8.3125 -10.109375 C 6.875 -10.109375 5.941406 -9.472656 5.515625 -8.203125 L 11.03125 -8.203125 C 10.625 -9.472656 9.71875 -10.109375 8.3125 -10.109375 Z M 8.3125 -10.109375 "/>
|
||||
</g>
|
||||
<g id="glyph-0-16">
|
||||
<path d="M 2.828125 -11.90625 C 4.054688 -13.25 5.671875 -13.921875 7.671875 -13.921875 C 9.128906 -13.921875 10.25 -13.570312 11.03125 -12.875 L 11.03125 -19.3125 L 15.734375 -19.3125 L 15.734375 0 L 11.375 0 L 11.375 -1.140625 C 10.65625 -0.191406 9.421875 0.28125 7.671875 0.28125 C 5.671875 0.28125 4.054688 -0.394531 2.828125 -1.75 C 1.597656 -3.101562 0.984375 -4.796875 0.984375 -6.828125 C 0.984375 -8.847656 1.597656 -10.539062 2.828125 -11.90625 Z M 6.546875 -4.625 C 7.085938 -4.039062 7.789062 -3.75 8.65625 -3.75 C 9.457031 -3.75 10.269531 -3.992188 11.09375 -4.484375 L 11.09375 -9.1875 C 10.34375 -9.65625 9.53125 -9.890625 8.65625 -9.890625 C 7.789062 -9.890625 7.085938 -9.597656 6.546875 -9.015625 C 6.003906 -8.429688 5.734375 -7.703125 5.734375 -6.828125 C 5.734375 -5.929688 6.003906 -5.195312 6.546875 -4.625 Z M 6.546875 -4.625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-17">
|
||||
<path d="M 3.140625 -11.90625 C 4.578125 -13.25 6.40625 -13.921875 8.625 -13.921875 C 10.28125 -13.921875 11.800781 -13.65625 13.1875 -13.125 L 13.1875 -9.046875 C 11.882812 -9.609375 10.644531 -9.890625 9.46875 -9.890625 C 8.269531 -9.890625 7.347656 -9.601562 6.703125 -9.03125 C 6.054688 -8.457031 5.734375 -7.722656 5.734375 -6.828125 C 5.734375 -5.910156 6.054688 -5.171875 6.703125 -4.609375 C 7.347656 -4.054688 8.269531 -3.78125 9.46875 -3.78125 C 10.582031 -3.78125 11.820312 -4.0625 13.1875 -4.625 L 13.1875 -0.5 C 11.800781 0.0195312 10.28125 0.28125 8.625 0.28125 C 6.40625 0.28125 4.578125 -0.394531 3.140625 -1.75 C 1.703125 -3.101562 0.984375 -4.796875 0.984375 -6.828125 C 0.984375 -8.867188 1.703125 -10.5625 3.140625 -11.90625 Z M 3.140625 -11.90625 "/>
|
||||
</g>
|
||||
<g id="glyph-0-18">
|
||||
<path d="M 1.546875 -13.25 C 1.546875 -15.050781 2.179688 -16.488281 3.453125 -17.5625 C 4.734375 -18.644531 6.398438 -19.1875 8.453125 -19.1875 C 10.660156 -19.1875 12.734375 -18.8125 14.671875 -18.0625 L 14.671875 -13.78125 C 12.578125 -14.613281 10.609375 -15.03125 8.765625 -15.03125 C 7.117188 -15.03125 6.296875 -14.582031 6.296875 -13.6875 C 6.296875 -13.238281 6.484375 -12.90625 6.859375 -12.6875 C 7.234375 -12.476562 7.882812 -12.234375 8.8125 -11.953125 L 10.1875 -11.5625 C 12 -11.0625 13.367188 -10.351562 14.296875 -9.4375 C 15.234375 -8.519531 15.703125 -7.332031 15.703125 -5.875 C 15.703125 -3.882812 15.007812 -2.335938 13.625 -1.234375 C 12.25 -0.128906 10.378906 0.421875 8.015625 0.421875 C 5.941406 0.421875 3.929688 0.046875 1.984375 -0.703125 L 1.984375 -5.046875 C 4.097656 -4.203125 6.097656 -3.78125 7.984375 -3.78125 C 8.953125 -3.78125 9.6875 -3.925781 10.1875 -4.21875 C 10.695312 -4.519531 10.953125 -4.929688 10.953125 -5.453125 C 10.953125 -5.941406 10.78125 -6.285156 10.4375 -6.484375 C 10.101562 -6.691406 9.441406 -6.929688 8.453125 -7.203125 L 6.9375 -7.609375 C 3.34375 -8.617188 1.546875 -10.5 1.546875 -13.25 Z M 1.546875 -13.25 "/>
|
||||
</g>
|
||||
<g id="glyph-0-19">
|
||||
<path d="M 3.953125 -16.375 C 5.847656 -18.25 8.367188 -19.1875 11.515625 -19.1875 C 13.878906 -19.1875 15.976562 -18.820312 17.8125 -18.09375 L 17.8125 -13.5 C 15.945312 -14.238281 14.03125 -14.609375 12.0625 -14.609375 C 10.195312 -14.609375 8.738281 -14.125 7.6875 -13.15625 C 6.644531 -12.1875 6.125 -10.925781 6.125 -9.375 C 6.125 -7.832031 6.644531 -6.570312 7.6875 -5.59375 C 8.738281 -4.625 10.195312 -4.140625 12.0625 -4.140625 C 14.03125 -4.140625 15.945312 -4.515625 17.8125 -5.265625 L 17.8125 -0.671875 C 15.976562 0.0546875 13.878906 0.421875 11.515625 0.421875 C 8.367188 0.421875 5.84375 -0.515625 3.9375 -2.390625 C 2.039062 -4.265625 1.09375 -6.59375 1.09375 -9.375 C 1.09375 -12.15625 2.046875 -14.488281 3.953125 -16.375 Z M 3.953125 -16.375 "/>
|
||||
</g>
|
||||
</g>
|
||||
<clipPath id="clip-0">
|
||||
<path clip-rule="nonzero" d="M 2 44 L 80 44 L 80 81 L 2 81 Z M 2 44 "/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-1">
|
||||
<path clip-rule="nonzero" d="M 20.523438 44.191406 C 10.375 44.191406 2.144531 52.421875 2.144531 62.570312 C 2.144531 72.71875 10.375 80.949219 20.523438 80.949219 L 60.722656 80.949219 C 70.871094 80.949219 79.097656 72.71875 79.097656 62.570312 C 79.097656 52.421875 70.871094 44.191406 60.722656 44.191406 Z M 20.523438 44.191406 "/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-pattern-0" gradientUnits="userSpaceOnUse" x1="0.00000772817" y1="0" x2="1.000005" y2="0" gradientTransform="matrix(-76.952225, 0, 0, -76.952225, 79.098595, 62.570047)">
|
||||
<stop offset="0" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.015625" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.0234375" stop-color="rgb(100%, 100%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.03125" stop-color="rgb(100%, 100%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.0390625" stop-color="rgb(100%, 100%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.046875" stop-color="rgb(100%, 99.607849%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.0546875" stop-color="rgb(100%, 99.607849%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.0625" stop-color="rgb(100%, 99.607849%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.0703125" stop-color="rgb(100%, 99.607849%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.078125" stop-color="rgb(100%, 99.607849%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.0859375" stop-color="rgb(100%, 99.607849%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.09375" stop-color="rgb(100%, 99.607849%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.101562" stop-color="rgb(100%, 99.607849%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.109375" stop-color="rgb(100%, 99.215698%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.117187" stop-color="rgb(100%, 99.215698%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.125" stop-color="rgb(99.607849%, 99.215698%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.132812" stop-color="rgb(99.607849%, 99.215698%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.140625" stop-color="rgb(99.607849%, 99.215698%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.148438" stop-color="rgb(99.607849%, 99.215698%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.15625" stop-color="rgb(99.607849%, 99.215698%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.164062" stop-color="rgb(99.607849%, 99.215698%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.171875" stop-color="rgb(99.607849%, 98.823547%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.179688" stop-color="rgb(99.607849%, 98.823547%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.1875" stop-color="rgb(99.607849%, 98.823547%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.195312" stop-color="rgb(99.607849%, 98.823547%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.203125" stop-color="rgb(99.607849%, 98.823547%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.210937" stop-color="rgb(99.607849%, 98.823547%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.21875" stop-color="rgb(99.607849%, 98.823547%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.226562" stop-color="rgb(99.607849%, 98.823547%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.234375" stop-color="rgb(99.607849%, 98.431396%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.242188" stop-color="rgb(99.607849%, 98.431396%, 90.588379%)" stop-opacity="1"/>
|
||||
<stop offset="0.25" stop-color="rgb(99.607849%, 98.431396%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.257812" stop-color="rgb(99.607849%, 98.431396%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.265625" stop-color="rgb(99.607849%, 98.431396%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.273438" stop-color="rgb(99.607849%, 98.431396%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.28125" stop-color="rgb(99.607849%, 98.431396%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.289062" stop-color="rgb(99.607849%, 98.431396%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.296875" stop-color="rgb(99.607849%, 98.431396%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.304688" stop-color="rgb(99.607849%, 98.431396%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.3125" stop-color="rgb(99.607849%, 98.431396%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.320312" stop-color="rgb(99.607849%, 98.039246%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.328125" stop-color="rgb(99.607849%, 98.039246%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.335938" stop-color="rgb(99.215698%, 98.039246%, 87.059021%)" stop-opacity="1"/>
|
||||
<stop offset="0.34375" stop-color="rgb(99.215698%, 98.039246%, 86.66687%)" stop-opacity="1"/>
|
||||
<stop offset="0.351562" stop-color="rgb(99.215698%, 98.039246%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.359375" stop-color="rgb(99.215698%, 98.039246%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.367188" stop-color="rgb(99.215698%, 98.039246%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.375" stop-color="rgb(99.215698%, 98.039246%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.382812" stop-color="rgb(99.215698%, 97.647095%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.390625" stop-color="rgb(99.215698%, 97.647095%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.398437" stop-color="rgb(99.215698%, 97.647095%, 84.313965%)" stop-opacity="1"/>
|
||||
<stop offset="0.40625" stop-color="rgb(99.215698%, 97.647095%, 83.921814%)" stop-opacity="1"/>
|
||||
<stop offset="0.414062" stop-color="rgb(99.215698%, 97.647095%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.421875" stop-color="rgb(99.215698%, 97.647095%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.429688" stop-color="rgb(99.215698%, 97.647095%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.4375" stop-color="rgb(99.215698%, 97.647095%, 82.745361%)" stop-opacity="1"/>
|
||||
<stop offset="0.445312" stop-color="rgb(99.215698%, 97.254944%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.453125" stop-color="rgb(99.215698%, 97.254944%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.460938" stop-color="rgb(99.215698%, 97.254944%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.46875" stop-color="rgb(99.215698%, 97.254944%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.476562" stop-color="rgb(99.215698%, 97.254944%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.484375" stop-color="rgb(99.215698%, 97.254944%, 80.784607%)" stop-opacity="1"/>
|
||||
<stop offset="0.492187" stop-color="rgb(98.823547%, 97.254944%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="rgb(98.823547%, 97.254944%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.507812" stop-color="rgb(98.823547%, 96.862793%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.515625" stop-color="rgb(98.823547%, 96.862793%, 79.608154%)" stop-opacity="1"/>
|
||||
<stop offset="0.523438" stop-color="rgb(98.823547%, 96.862793%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.53125" stop-color="rgb(98.823547%, 96.862793%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.539062" stop-color="rgb(98.823547%, 96.862793%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.546875" stop-color="rgb(98.823547%, 96.862793%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.554688" stop-color="rgb(98.823547%, 96.862793%, 78.039551%)" stop-opacity="1"/>
|
||||
<stop offset="0.5625" stop-color="rgb(98.823547%, 96.862793%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.570312" stop-color="rgb(98.823547%, 96.862793%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.578125" stop-color="rgb(98.823547%, 96.862793%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.585938" stop-color="rgb(98.823547%, 96.470642%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="0.59375" stop-color="rgb(98.823547%, 96.470642%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.601562" stop-color="rgb(98.823547%, 96.470642%, 76.078796%)" stop-opacity="1"/>
|
||||
<stop offset="0.609375" stop-color="rgb(98.823547%, 96.470642%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="0.617188" stop-color="rgb(98.431396%, 96.470642%, 75.294495%)" stop-opacity="1"/>
|
||||
<stop offset="0.625" stop-color="rgb(98.431396%, 96.470642%, 75.294495%)" stop-opacity="1"/>
|
||||
<stop offset="0.632812" stop-color="rgb(98.431396%, 96.470642%, 74.902344%)" stop-opacity="1"/>
|
||||
<stop offset="0.640625" stop-color="rgb(98.431396%, 96.470642%, 74.510193%)" stop-opacity="1"/>
|
||||
<stop offset="0.648438" stop-color="rgb(98.431396%, 96.078491%, 74.118042%)" stop-opacity="1"/>
|
||||
<stop offset="0.65625" stop-color="rgb(98.431396%, 96.078491%, 73.725891%)" stop-opacity="1"/>
|
||||
<stop offset="0.664062" stop-color="rgb(98.431396%, 96.078491%, 73.33374%)" stop-opacity="1"/>
|
||||
<stop offset="0.671875" stop-color="rgb(98.431396%, 96.078491%, 73.33374%)" stop-opacity="1"/>
|
||||
<stop offset="0.679688" stop-color="rgb(98.431396%, 96.078491%, 72.941589%)" stop-opacity="1"/>
|
||||
<stop offset="0.6875" stop-color="rgb(98.431396%, 96.078491%, 72.549438%)" stop-opacity="1"/>
|
||||
<stop offset="0.695312" stop-color="rgb(98.431396%, 96.078491%, 72.157288%)" stop-opacity="1"/>
|
||||
<stop offset="0.703125" stop-color="rgb(98.431396%, 96.078491%, 71.765137%)" stop-opacity="1"/>
|
||||
<stop offset="0.710938" stop-color="rgb(98.431396%, 96.078491%, 71.372986%)" stop-opacity="1"/>
|
||||
<stop offset="0.71875" stop-color="rgb(98.431396%, 96.078491%, 71.372986%)" stop-opacity="1"/>
|
||||
<stop offset="0.726562" stop-color="rgb(98.431396%, 95.68634%, 70.980835%)" stop-opacity="1"/>
|
||||
<stop offset="0.734375" stop-color="rgb(98.431396%, 95.68634%, 70.588684%)" stop-opacity="1"/>
|
||||
<stop offset="0.742188" stop-color="rgb(98.431396%, 95.68634%, 70.196533%)" stop-opacity="1"/>
|
||||
<stop offset="0.75" stop-color="rgb(98.431396%, 95.68634%, 69.804382%)" stop-opacity="1"/>
|
||||
<stop offset="0.757812" stop-color="rgb(98.431396%, 95.68634%, 69.412231%)" stop-opacity="1"/>
|
||||
<stop offset="0.765625" stop-color="rgb(98.431396%, 95.68634%, 69.412231%)" stop-opacity="1"/>
|
||||
<stop offset="0.773437" stop-color="rgb(98.431396%, 95.68634%, 69.020081%)" stop-opacity="1"/>
|
||||
<stop offset="0.78125" stop-color="rgb(98.431396%, 95.68634%, 68.62793%)" stop-opacity="1"/>
|
||||
<stop offset="0.789062" stop-color="rgb(98.431396%, 95.68634%, 68.62793%)" stop-opacity="1"/>
|
||||
<stop offset="0.796875" stop-color="rgb(98.039246%, 95.68634%, 68.235779%)" stop-opacity="1"/>
|
||||
<stop offset="0.804688" stop-color="rgb(98.039246%, 95.68634%, 67.843628%)" stop-opacity="1"/>
|
||||
<stop offset="0.8125" stop-color="rgb(98.039246%, 95.294189%, 67.451477%)" stop-opacity="1"/>
|
||||
<stop offset="0.820312" stop-color="rgb(98.039246%, 95.294189%, 67.059326%)" stop-opacity="1"/>
|
||||
<stop offset="0.828125" stop-color="rgb(98.039246%, 95.294189%, 66.667175%)" stop-opacity="1"/>
|
||||
<stop offset="0.835938" stop-color="rgb(98.039246%, 95.294189%, 66.667175%)" stop-opacity="1"/>
|
||||
<stop offset="0.84375" stop-color="rgb(98.039246%, 95.294189%, 66.275024%)" stop-opacity="1"/>
|
||||
<stop offset="0.851562" stop-color="rgb(98.039246%, 95.294189%, 65.882874%)" stop-opacity="1"/>
|
||||
<stop offset="0.859375" stop-color="rgb(98.039246%, 95.294189%, 65.490723%)" stop-opacity="1"/>
|
||||
<stop offset="0.867187" stop-color="rgb(98.039246%, 95.294189%, 65.490723%)" stop-opacity="1"/>
|
||||
<stop offset="0.875" stop-color="rgb(98.039246%, 95.294189%, 65.098572%)" stop-opacity="1"/>
|
||||
<stop offset="0.882812" stop-color="rgb(98.039246%, 95.294189%, 64.706421%)" stop-opacity="1"/>
|
||||
<stop offset="0.890625" stop-color="rgb(98.039246%, 94.902039%, 64.31427%)" stop-opacity="1"/>
|
||||
<stop offset="0.898438" stop-color="rgb(98.039246%, 94.902039%, 64.31427%)" stop-opacity="1"/>
|
||||
<stop offset="0.90625" stop-color="rgb(98.039246%, 94.902039%, 63.922119%)" stop-opacity="1"/>
|
||||
<stop offset="0.914062" stop-color="rgb(98.039246%, 94.902039%, 63.529968%)" stop-opacity="1"/>
|
||||
<stop offset="0.921875" stop-color="rgb(98.039246%, 94.902039%, 63.137817%)" stop-opacity="1"/>
|
||||
<stop offset="0.9375" stop-color="rgb(98.039246%, 94.902039%, 62.745667%)" stop-opacity="1"/>
|
||||
<stop offset="0.945312" stop-color="rgb(98.039246%, 94.902039%, 62.745667%)" stop-opacity="1"/>
|
||||
<stop offset="0.953125" stop-color="rgb(98.039246%, 94.902039%, 62.353516%)" stop-opacity="1"/>
|
||||
<stop offset="0.960937" stop-color="rgb(98.039246%, 94.902039%, 61.961365%)" stop-opacity="1"/>
|
||||
<stop offset="0.96875" stop-color="rgb(98.039246%, 94.902039%, 61.569214%)" stop-opacity="1"/>
|
||||
<stop offset="0.984375" stop-color="rgb(98.039246%, 94.509888%, 61.177063%)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(98.039246%, 94.509888%, 60.784912%)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip-2">
|
||||
<path clip-rule="nonzero" d="M 13 15 L 80 15 L 80 81 L 13 81 Z M 13 15 "/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-3">
|
||||
<path clip-rule="nonzero" d="M 19.300781 21.152344 C 12.125 28.328125 12.125 39.964844 19.300781 47.140625 L 47.726562 75.566406 C 51.316406 79.15625 56.019531 80.949219 60.722656 80.949219 C 65.425781 80.949219 70.128906 79.15625 73.714844 75.566406 C 80.894531 68.386719 80.894531 56.753906 73.714844 49.574219 L 45.292969 21.152344 C 41.703125 17.5625 37 15.769531 32.296875 15.769531 C 27.59375 15.769531 22.890625 17.5625 19.300781 21.152344 "/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-pattern-1" gradientUnits="userSpaceOnUse" x1="-0.115399" y1="0" x2="1.115408" y2="0" gradientTransform="matrix(-54.414318, -54.414318, 54.414318, -54.414318, 73.71669, 75.565292)">
|
||||
<stop offset="0" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.0625" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.09375" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.0976562" stop-color="rgb(99.607849%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.101562" stop-color="rgb(99.607849%, 99.607849%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.105469" stop-color="rgb(99.215698%, 99.607849%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.109375" stop-color="rgb(99.215698%, 99.607849%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.113281" stop-color="rgb(98.823547%, 99.215698%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.117188" stop-color="rgb(98.431396%, 99.215698%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.121094" stop-color="rgb(98.431396%, 99.215698%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.125" stop-color="rgb(98.039246%, 99.215698%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.128906" stop-color="rgb(98.039246%, 98.823547%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.132812" stop-color="rgb(97.647095%, 98.823547%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.136719" stop-color="rgb(97.254944%, 98.823547%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.140625" stop-color="rgb(97.254944%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.144531" stop-color="rgb(96.862793%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.148438" stop-color="rgb(96.470642%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.152344" stop-color="rgb(96.078491%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.15625" stop-color="rgb(95.68634%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.160156" stop-color="rgb(95.68634%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.164062" stop-color="rgb(95.294189%, 98.431396%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.167969" stop-color="rgb(94.902039%, 98.039246%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.171875" stop-color="rgb(94.902039%, 98.039246%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.175781" stop-color="rgb(94.509888%, 98.039246%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.179688" stop-color="rgb(94.509888%, 97.647095%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.183594" stop-color="rgb(94.117737%, 97.647095%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.1875" stop-color="rgb(93.725586%, 97.647095%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.191406" stop-color="rgb(93.725586%, 97.254944%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.195312" stop-color="rgb(93.333435%, 97.254944%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.199219" stop-color="rgb(93.333435%, 97.254944%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.203125" stop-color="rgb(92.941284%, 97.254944%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.207031" stop-color="rgb(92.549133%, 96.862793%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.210937" stop-color="rgb(92.549133%, 96.862793%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.214844" stop-color="rgb(92.156982%, 96.862793%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.21875" stop-color="rgb(91.764832%, 96.470642%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.222656" stop-color="rgb(91.372681%, 96.470642%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.226563" stop-color="rgb(90.98053%, 96.470642%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.230469" stop-color="rgb(90.98053%, 96.078491%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.234375" stop-color="rgb(90.588379%, 96.078491%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.238281" stop-color="rgb(90.588379%, 96.078491%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.242188" stop-color="rgb(90.196228%, 95.68634%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.246094" stop-color="rgb(89.804077%, 95.68634%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.25" stop-color="rgb(89.804077%, 95.68634%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.253906" stop-color="rgb(89.411926%, 95.68634%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.257812" stop-color="rgb(89.411926%, 95.294189%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.261719" stop-color="rgb(89.019775%, 95.294189%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.265625" stop-color="rgb(88.627625%, 95.294189%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.269531" stop-color="rgb(88.627625%, 94.902039%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.273438" stop-color="rgb(88.235474%, 94.902039%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.277344" stop-color="rgb(88.235474%, 94.902039%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.28125" stop-color="rgb(87.843323%, 94.509888%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.285156" stop-color="rgb(87.451172%, 94.509888%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.289062" stop-color="rgb(87.451172%, 94.509888%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.292969" stop-color="rgb(87.059021%, 94.509888%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.296875" stop-color="rgb(86.66687%, 94.117737%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.300781" stop-color="rgb(86.66687%, 94.117737%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.304688" stop-color="rgb(86.274719%, 94.117737%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.308594" stop-color="rgb(85.882568%, 93.725586%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.3125" stop-color="rgb(85.490417%, 93.725586%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.316406" stop-color="rgb(85.098267%, 93.725586%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.320312" stop-color="rgb(85.098267%, 93.333435%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.324219" stop-color="rgb(84.706116%, 93.333435%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.328125" stop-color="rgb(84.706116%, 93.333435%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.332031" stop-color="rgb(84.313965%, 93.333435%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.335938" stop-color="rgb(83.921814%, 92.941284%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.339844" stop-color="rgb(83.921814%, 92.941284%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.34375" stop-color="rgb(83.529663%, 92.941284%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.347656" stop-color="rgb(83.529663%, 92.549133%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.351562" stop-color="rgb(83.137512%, 92.549133%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.355469" stop-color="rgb(82.745361%, 92.549133%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.359375" stop-color="rgb(82.35321%, 92.156982%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.363281" stop-color="rgb(81.96106%, 92.156982%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.367188" stop-color="rgb(81.96106%, 92.156982%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.371094" stop-color="rgb(81.568909%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.375" stop-color="rgb(81.176758%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.378906" stop-color="rgb(81.176758%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.382812" stop-color="rgb(80.784607%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.386719" stop-color="rgb(80.784607%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.390625" stop-color="rgb(80.392456%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.394531" stop-color="rgb(80.000305%, 91.764832%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.398438" stop-color="rgb(80.000305%, 91.372681%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.402344" stop-color="rgb(79.608154%, 91.372681%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.40625" stop-color="rgb(79.608154%, 91.372681%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.410156" stop-color="rgb(79.216003%, 90.98053%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.414062" stop-color="rgb(78.823853%, 90.98053%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.417969" stop-color="rgb(78.431702%, 90.98053%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.421875" stop-color="rgb(78.431702%, 90.98053%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.425781" stop-color="rgb(78.039551%, 90.588379%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.429688" stop-color="rgb(77.6474%, 90.588379%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.433594" stop-color="rgb(77.255249%, 90.588379%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.4375" stop-color="rgb(77.255249%, 90.196228%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.441406" stop-color="rgb(76.863098%, 90.196228%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.445312" stop-color="rgb(76.470947%, 90.196228%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.449219" stop-color="rgb(76.470947%, 89.804077%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.453125" stop-color="rgb(76.078796%, 89.804077%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.457031" stop-color="rgb(75.686646%, 89.804077%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.460938" stop-color="rgb(75.686646%, 89.804077%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.464844" stop-color="rgb(75.294495%, 89.411926%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.46875" stop-color="rgb(74.902344%, 89.411926%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.472656" stop-color="rgb(74.510193%, 89.411926%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.476563" stop-color="rgb(74.510193%, 89.019775%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.480469" stop-color="rgb(74.118042%, 89.019775%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.484375" stop-color="rgb(73.725891%, 89.019775%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.488281" stop-color="rgb(73.33374%, 88.627625%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.492187" stop-color="rgb(72.941589%, 88.627625%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.496094" stop-color="rgb(72.941589%, 88.627625%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="rgb(72.549438%, 88.235474%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.503906" stop-color="rgb(72.157288%, 88.235474%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.507812" stop-color="rgb(72.157288%, 88.235474%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.511719" stop-color="rgb(71.765137%, 88.235474%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.515625" stop-color="rgb(71.765137%, 87.843323%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.519531" stop-color="rgb(71.372986%, 87.843323%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.523437" stop-color="rgb(70.980835%, 87.843323%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.527344" stop-color="rgb(70.588684%, 87.451172%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.53125" stop-color="rgb(70.196533%, 87.451172%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.535156" stop-color="rgb(70.196533%, 87.451172%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.539062" stop-color="rgb(69.804382%, 87.059021%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.542969" stop-color="rgb(69.412231%, 87.059021%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.546875" stop-color="rgb(69.412231%, 87.059021%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.550781" stop-color="rgb(69.020081%, 87.059021%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.554687" stop-color="rgb(68.62793%, 86.66687%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.558594" stop-color="rgb(68.235779%, 86.66687%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.5625" stop-color="rgb(67.843628%, 86.66687%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.566406" stop-color="rgb(67.843628%, 86.66687%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.570312" stop-color="rgb(67.451477%, 86.66687%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.574219" stop-color="rgb(67.059326%, 86.66687%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.578125" stop-color="rgb(66.667175%, 86.274719%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.582031" stop-color="rgb(66.275024%, 86.274719%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.585937" stop-color="rgb(66.275024%, 86.274719%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.589844" stop-color="rgb(65.882874%, 86.274719%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.59375" stop-color="rgb(65.490723%, 85.882568%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.597656" stop-color="rgb(65.490723%, 85.882568%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.601562" stop-color="rgb(65.098572%, 85.882568%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.605469" stop-color="rgb(64.706421%, 85.490417%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.609375" stop-color="rgb(64.31427%, 85.490417%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.613281" stop-color="rgb(63.922119%, 85.490417%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.617188" stop-color="rgb(63.922119%, 85.098267%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.621094" stop-color="rgb(63.529968%, 85.098267%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.625" stop-color="rgb(63.137817%, 85.098267%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.628906" stop-color="rgb(63.137817%, 85.098267%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.632812" stop-color="rgb(62.745667%, 84.706116%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.636719" stop-color="rgb(62.353516%, 84.706116%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.640625" stop-color="rgb(61.961365%, 84.706116%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.644531" stop-color="rgb(61.961365%, 84.313965%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.648438" stop-color="rgb(61.569214%, 84.313965%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.652344" stop-color="rgb(61.177063%, 84.313965%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.65625" stop-color="rgb(60.784912%, 83.921814%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.660156" stop-color="rgb(60.392761%, 83.921814%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.664063" stop-color="rgb(60.392761%, 83.921814%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.667969" stop-color="rgb(60.00061%, 83.529663%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.671875" stop-color="rgb(59.608459%, 83.529663%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.675781" stop-color="rgb(59.608459%, 83.529663%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.679688" stop-color="rgb(59.216309%, 83.529663%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.683594" stop-color="rgb(58.824158%, 83.137512%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.6875" stop-color="rgb(58.432007%, 83.137512%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.691406" stop-color="rgb(58.039856%, 83.137512%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.695313" stop-color="rgb(58.039856%, 83.137512%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.699219" stop-color="rgb(57.647705%, 83.137512%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.703125" stop-color="rgb(57.255554%, 83.137512%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.707031" stop-color="rgb(56.863403%, 82.745361%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.710938" stop-color="rgb(56.471252%, 82.745361%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.714844" stop-color="rgb(56.471252%, 82.745361%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.71875" stop-color="rgb(56.079102%, 82.745361%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.722656" stop-color="rgb(55.686951%, 82.35321%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.726563" stop-color="rgb(55.686951%, 82.35321%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.730469" stop-color="rgb(55.2948%, 82.35321%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.734375" stop-color="rgb(54.902649%, 81.96106%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.738281" stop-color="rgb(54.510498%, 81.96106%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.742188" stop-color="rgb(54.118347%, 81.96106%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.746094" stop-color="rgb(54.118347%, 81.568909%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.75" stop-color="rgb(53.726196%, 81.568909%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.753906" stop-color="rgb(53.334045%, 81.568909%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.757812" stop-color="rgb(53.334045%, 81.568909%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.761719" stop-color="rgb(52.941895%, 81.176758%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.765625" stop-color="rgb(52.549744%, 81.176758%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.769531" stop-color="rgb(52.157593%, 81.176758%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.773437" stop-color="rgb(52.157593%, 81.176758%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.777344" stop-color="rgb(51.765442%, 81.176758%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.78125" stop-color="rgb(51.373291%, 81.176758%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.785156" stop-color="rgb(50.98114%, 80.784607%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.789062" stop-color="rgb(50.588989%, 80.784607%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.792969" stop-color="rgb(50.588989%, 80.784607%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.796875" stop-color="rgb(50.196838%, 80.392456%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.800781" stop-color="rgb(49.803162%, 80.392456%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.804687" stop-color="rgb(49.803162%, 80.392456%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.808594" stop-color="rgb(49.411011%, 80.392456%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.8125" stop-color="rgb(49.01886%, 80.000305%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.816406" stop-color="rgb(48.626709%, 80.000305%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.820312" stop-color="rgb(48.234558%, 80.000305%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.824219" stop-color="rgb(47.842407%, 80.000305%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.828125" stop-color="rgb(47.450256%, 80.000305%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.832031" stop-color="rgb(47.450256%, 80.000305%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.835937" stop-color="rgb(47.058105%, 79.608154%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.839844" stop-color="rgb(46.665955%, 79.608154%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.84375" stop-color="rgb(46.665955%, 79.608154%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.847656" stop-color="rgb(46.273804%, 79.608154%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.851562" stop-color="rgb(45.881653%, 79.216003%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.855469" stop-color="rgb(45.489502%, 79.216003%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.859375" stop-color="rgb(45.097351%, 79.216003%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.863281" stop-color="rgb(45.097351%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.867188" stop-color="rgb(44.7052%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.871094" stop-color="rgb(44.313049%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.875" stop-color="rgb(43.920898%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.878906" stop-color="rgb(43.528748%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.882812" stop-color="rgb(43.528748%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.886719" stop-color="rgb(43.136597%, 78.823853%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.890625" stop-color="rgb(42.744446%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.894531" stop-color="rgb(42.744446%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.898438" stop-color="rgb(42.352295%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.902344" stop-color="rgb(42.352295%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.90625" stop-color="rgb(41.960144%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.9375" stop-color="rgb(41.960144%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(41.960144%, 78.431702%, 90.98053%)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip-4">
|
||||
<path clip-rule="nonzero" d="M 42 3 L 80 3 L 80 81 L 42 81 Z M 42 3 "/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-5">
|
||||
<path clip-rule="nonzero" d="M 42.34375 22.371094 L 42.34375 62.570312 C 42.34375 72.71875 50.570312 80.949219 60.722656 80.949219 C 70.871094 80.949219 79.097656 72.71875 79.097656 62.570312 L 79.097656 22.371094 C 79.097656 12.222656 70.871094 3.996094 60.722656 3.996094 C 50.570312 3.996094 42.34375 12.222656 42.34375 22.371094 "/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-pattern-2" gradientUnits="userSpaceOnUse" x1="0.00000321492" y1="0" x2="0.999994" y2="0" gradientTransform="matrix(0, -76.95372, 76.95372, 0, 60.721073, 80.947947)">
|
||||
<stop offset="0" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.0078125" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.015625" stop-color="rgb(99.607849%, 99.607849%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.0234375" stop-color="rgb(99.607849%, 99.215698%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.03125" stop-color="rgb(99.215698%, 98.823547%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.0390625" stop-color="rgb(98.823547%, 98.431396%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.046875" stop-color="rgb(98.431396%, 98.039246%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.0546875" stop-color="rgb(98.431396%, 97.647095%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.0625" stop-color="rgb(98.039246%, 97.254944%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.0703125" stop-color="rgb(97.647095%, 96.862793%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.078125" stop-color="rgb(97.254944%, 96.470642%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.0859375" stop-color="rgb(97.254944%, 96.078491%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.09375" stop-color="rgb(96.862793%, 95.68634%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.101562" stop-color="rgb(96.470642%, 95.294189%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.109375" stop-color="rgb(96.078491%, 94.902039%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.117188" stop-color="rgb(96.078491%, 94.509888%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.125" stop-color="rgb(95.68634%, 94.117737%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.132812" stop-color="rgb(95.294189%, 93.725586%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.140625" stop-color="rgb(94.902039%, 93.333435%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.148438" stop-color="rgb(94.902039%, 92.941284%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.15625" stop-color="rgb(94.509888%, 92.549133%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.164062" stop-color="rgb(94.117737%, 92.156982%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.171875" stop-color="rgb(93.725586%, 91.764832%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.179688" stop-color="rgb(93.725586%, 91.372681%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.1875" stop-color="rgb(93.333435%, 90.98053%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.195312" stop-color="rgb(92.941284%, 90.588379%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.203125" stop-color="rgb(92.549133%, 90.196228%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.210937" stop-color="rgb(92.549133%, 89.804077%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.21875" stop-color="rgb(92.156982%, 89.411926%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.226562" stop-color="rgb(91.764832%, 89.019775%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.234375" stop-color="rgb(91.372681%, 88.627625%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.242187" stop-color="rgb(91.372681%, 88.235474%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.25" stop-color="rgb(90.98053%, 87.843323%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.257812" stop-color="rgb(90.98053%, 87.451172%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.265625" stop-color="rgb(90.588379%, 87.451172%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.273438" stop-color="rgb(90.196228%, 87.059021%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.28125" stop-color="rgb(90.196228%, 86.66687%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.289062" stop-color="rgb(89.804077%, 86.274719%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.296875" stop-color="rgb(89.411926%, 85.882568%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.304687" stop-color="rgb(89.019775%, 85.490417%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.3125" stop-color="rgb(89.019775%, 85.098267%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.320312" stop-color="rgb(88.627625%, 84.706116%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.328125" stop-color="rgb(88.627625%, 84.313965%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.335937" stop-color="rgb(88.235474%, 83.921814%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.34375" stop-color="rgb(87.843323%, 83.529663%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.351562" stop-color="rgb(87.451172%, 83.137512%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.359375" stop-color="rgb(87.451172%, 82.745361%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.367188" stop-color="rgb(87.059021%, 82.35321%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.375" stop-color="rgb(87.059021%, 81.96106%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.382812" stop-color="rgb(86.66687%, 81.568909%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.390625" stop-color="rgb(86.274719%, 81.176758%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.398437" stop-color="rgb(85.882568%, 80.784607%, 90.588379%)" stop-opacity="1"/>
|
||||
<stop offset="0.40625" stop-color="rgb(85.882568%, 80.784607%, 90.588379%)" stop-opacity="1"/>
|
||||
<stop offset="0.414062" stop-color="rgb(85.490417%, 80.392456%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.421875" stop-color="rgb(85.490417%, 80.000305%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.429688" stop-color="rgb(85.098267%, 79.608154%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.4375" stop-color="rgb(84.706116%, 79.216003%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.445312" stop-color="rgb(84.313965%, 78.823853%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.453125" stop-color="rgb(84.313965%, 78.431702%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.460938" stop-color="rgb(83.921814%, 78.039551%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.46875" stop-color="rgb(83.921814%, 77.6474%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.476562" stop-color="rgb(83.529663%, 77.255249%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.484375" stop-color="rgb(83.529663%, 76.863098%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.492188" stop-color="rgb(83.137512%, 76.470947%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="rgb(82.745361%, 76.470947%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.507812" stop-color="rgb(82.745361%, 76.078796%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.515625" stop-color="rgb(82.35321%, 76.078796%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.523438" stop-color="rgb(81.96106%, 75.686646%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.53125" stop-color="rgb(81.96106%, 75.294495%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.539062" stop-color="rgb(81.568909%, 74.902344%, 87.059021%)" stop-opacity="1"/>
|
||||
<stop offset="0.546875" stop-color="rgb(81.568909%, 74.510193%, 87.059021%)" stop-opacity="1"/>
|
||||
<stop offset="0.554687" stop-color="rgb(81.176758%, 74.118042%, 86.66687%)" stop-opacity="1"/>
|
||||
<stop offset="0.5625" stop-color="rgb(81.176758%, 73.725891%, 86.66687%)" stop-opacity="1"/>
|
||||
<stop offset="0.570313" stop-color="rgb(80.784607%, 73.33374%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.578125" stop-color="rgb(80.784607%, 73.33374%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.585938" stop-color="rgb(80.392456%, 72.941589%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.59375" stop-color="rgb(80.000305%, 72.549438%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.601562" stop-color="rgb(79.608154%, 72.157288%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.609375" stop-color="rgb(79.608154%, 71.765137%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.617188" stop-color="rgb(79.216003%, 71.372986%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.625" stop-color="rgb(79.216003%, 70.980835%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.632812" stop-color="rgb(78.823853%, 70.588684%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.640625" stop-color="rgb(78.823853%, 70.588684%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.648437" stop-color="rgb(78.431702%, 70.196533%, 84.313965%)" stop-opacity="1"/>
|
||||
<stop offset="0.65625" stop-color="rgb(78.431702%, 69.804382%, 84.313965%)" stop-opacity="1"/>
|
||||
<stop offset="0.664063" stop-color="rgb(78.039551%, 69.412231%, 83.921814%)" stop-opacity="1"/>
|
||||
<stop offset="0.671875" stop-color="rgb(78.039551%, 69.020081%, 83.921814%)" stop-opacity="1"/>
|
||||
<stop offset="0.679688" stop-color="rgb(77.6474%, 68.62793%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.6875" stop-color="rgb(77.255249%, 68.62793%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.695312" stop-color="rgb(76.863098%, 68.235779%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.703125" stop-color="rgb(76.863098%, 67.843628%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.710938" stop-color="rgb(76.470947%, 67.451477%, 82.745361%)" stop-opacity="1"/>
|
||||
<stop offset="0.71875" stop-color="rgb(76.470947%, 67.059326%, 82.745361%)" stop-opacity="1"/>
|
||||
<stop offset="0.726562" stop-color="rgb(76.078796%, 66.667175%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.734375" stop-color="rgb(76.078796%, 66.667175%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.742188" stop-color="rgb(75.686646%, 66.275024%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.75" stop-color="rgb(75.686646%, 65.882874%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.757813" stop-color="rgb(75.686646%, 65.490723%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.765625" stop-color="rgb(75.294495%, 65.098572%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.773438" stop-color="rgb(75.294495%, 65.098572%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.78125" stop-color="rgb(74.902344%, 64.706421%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.789062" stop-color="rgb(74.902344%, 64.31427%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.796875" stop-color="rgb(74.510193%, 63.922119%, 80.784607%)" stop-opacity="1"/>
|
||||
<stop offset="0.804688" stop-color="rgb(74.510193%, 63.922119%, 80.784607%)" stop-opacity="1"/>
|
||||
<stop offset="0.8125" stop-color="rgb(74.118042%, 63.529968%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.820312" stop-color="rgb(74.118042%, 63.137817%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.828125" stop-color="rgb(73.725891%, 62.745667%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.835938" stop-color="rgb(73.33374%, 62.353516%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.84375" stop-color="rgb(72.941589%, 61.961365%, 79.608154%)" stop-opacity="1"/>
|
||||
<stop offset="0.851563" stop-color="rgb(72.941589%, 61.961365%, 79.608154%)" stop-opacity="1"/>
|
||||
<stop offset="0.859375" stop-color="rgb(72.549438%, 61.569214%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.867188" stop-color="rgb(72.549438%, 61.177063%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.875" stop-color="rgb(72.157288%, 60.784912%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.890625" stop-color="rgb(71.765137%, 60.392761%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.898437" stop-color="rgb(71.765137%, 60.392761%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.90625" stop-color="rgb(71.765137%, 60.00061%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.914063" stop-color="rgb(71.765137%, 59.608459%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.921875" stop-color="rgb(71.372986%, 59.216309%, 78.039551%)" stop-opacity="1"/>
|
||||
<stop offset="0.9375" stop-color="rgb(70.980835%, 58.824158%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.945312" stop-color="rgb(70.980835%, 58.824158%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.953125" stop-color="rgb(70.588684%, 58.432007%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.960938" stop-color="rgb(70.588684%, 58.039856%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.96875" stop-color="rgb(70.196533%, 57.647705%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="0.984375" stop-color="rgb(69.804382%, 57.255554%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(69.804382%, 56.863403%, 76.470947%)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip-6">
|
||||
<path clip-rule="nonzero" d="M 42 15 L 108 15 L 108 81 L 42 81 Z M 42 15 "/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-7">
|
||||
<path clip-rule="nonzero" d="M 76.152344 21.152344 L 47.726562 49.574219 C 40.550781 56.75 40.550781 68.386719 47.726562 75.566406 C 51.316406 79.15625 56.019531 80.949219 60.722656 80.949219 C 65.425781 80.949219 70.128906 79.152344 73.714844 75.566406 L 102.140625 47.140625 C 109.316406 39.964844 109.316406 28.328125 102.140625 21.152344 C 98.550781 17.5625 93.847656 15.769531 89.144531 15.769531 C 84.441406 15.769531 79.738281 17.5625 76.152344 21.152344 "/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-pattern-3" gradientUnits="userSpaceOnUse" x1="-0.115406" y1="0" x2="1.115401" y2="0" gradientTransform="matrix(54.414692, -54.414692, 54.414692, 54.414692, 47.726387, 75.56548)">
|
||||
<stop offset="0" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.0625" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.09375" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.101562" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.109375" stop-color="rgb(100%, 99.607849%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.113281" stop-color="rgb(100%, 99.215698%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.117188" stop-color="rgb(100%, 98.823547%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.125" stop-color="rgb(100%, 98.823547%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.132812" stop-color="rgb(100%, 98.431396%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.140625" stop-color="rgb(100%, 98.431396%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.148438" stop-color="rgb(99.607849%, 98.039246%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.152344" stop-color="rgb(99.607849%, 97.647095%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.15625" stop-color="rgb(99.607849%, 97.254944%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.164062" stop-color="rgb(99.607849%, 97.254944%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.171875" stop-color="rgb(99.607849%, 96.862793%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.175781" stop-color="rgb(99.607849%, 96.862793%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.179687" stop-color="rgb(99.607849%, 96.470642%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.1875" stop-color="rgb(99.607849%, 96.470642%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.191406" stop-color="rgb(99.607849%, 96.078491%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.195312" stop-color="rgb(99.607849%, 95.68634%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.203125" stop-color="rgb(99.607849%, 95.68634%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.210938" stop-color="rgb(99.607849%, 95.294189%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.214844" stop-color="rgb(99.607849%, 94.902039%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.21875" stop-color="rgb(99.607849%, 94.902039%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.226562" stop-color="rgb(99.607849%, 94.509888%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.234375" stop-color="rgb(99.215698%, 94.117737%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.242187" stop-color="rgb(99.215698%, 94.117737%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.25" stop-color="rgb(99.215698%, 93.725586%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.253906" stop-color="rgb(99.215698%, 93.333435%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.257812" stop-color="rgb(99.215698%, 93.333435%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.265625" stop-color="rgb(99.215698%, 92.941284%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.273437" stop-color="rgb(99.215698%, 92.549133%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.28125" stop-color="rgb(99.215698%, 92.549133%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.289062" stop-color="rgb(99.215698%, 92.156982%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.292969" stop-color="rgb(99.215698%, 91.764832%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.296875" stop-color="rgb(99.215698%, 91.372681%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.304687" stop-color="rgb(99.215698%, 91.372681%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.3125" stop-color="rgb(98.823547%, 90.98053%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.316406" stop-color="rgb(98.823547%, 90.588379%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.320312" stop-color="rgb(98.823547%, 90.588379%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.324219" stop-color="rgb(98.823547%, 90.196228%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.328125" stop-color="rgb(98.823547%, 90.196228%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.335937" stop-color="rgb(98.823547%, 90.196228%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.34375" stop-color="rgb(98.823547%, 89.804077%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.351562" stop-color="rgb(98.823547%, 89.411926%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.355469" stop-color="rgb(98.823547%, 89.019775%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.359375" stop-color="rgb(98.823547%, 89.019775%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.363281" stop-color="rgb(98.823547%, 88.627625%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.367187" stop-color="rgb(98.823547%, 88.627625%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.375" stop-color="rgb(98.823547%, 88.235474%, 90.588379%)" stop-opacity="1"/>
|
||||
<stop offset="0.378906" stop-color="rgb(98.823547%, 88.235474%, 90.588379%)" stop-opacity="1"/>
|
||||
<stop offset="0.382812" stop-color="rgb(98.823547%, 88.235474%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.386719" stop-color="rgb(98.431396%, 87.843323%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.390625" stop-color="rgb(98.431396%, 87.843323%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.394531" stop-color="rgb(98.431396%, 87.451172%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.398438" stop-color="rgb(98.431396%, 87.451172%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.40625" stop-color="rgb(98.431396%, 87.059021%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.414062" stop-color="rgb(98.431396%, 86.66687%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.421875" stop-color="rgb(98.431396%, 86.66687%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.429687" stop-color="rgb(98.431396%, 86.274719%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.433594" stop-color="rgb(98.431396%, 85.882568%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.4375" stop-color="rgb(98.431396%, 85.882568%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.445312" stop-color="rgb(98.431396%, 85.490417%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.453125" stop-color="rgb(98.039246%, 85.098267%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.457031" stop-color="rgb(98.039246%, 85.098267%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.460937" stop-color="rgb(98.039246%, 84.706116%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.464844" stop-color="rgb(98.039246%, 84.313965%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.46875" stop-color="rgb(98.039246%, 84.313965%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.476562" stop-color="rgb(98.039246%, 84.313965%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.484375" stop-color="rgb(98.039246%, 83.921814%, 87.059021%)" stop-opacity="1"/>
|
||||
<stop offset="0.488281" stop-color="rgb(98.039246%, 83.529663%, 86.66687%)" stop-opacity="1"/>
|
||||
<stop offset="0.492188" stop-color="rgb(98.039246%, 83.529663%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.496094" stop-color="rgb(98.039246%, 83.529663%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="rgb(98.039246%, 83.137512%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.503906" stop-color="rgb(98.039246%, 82.745361%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.507812" stop-color="rgb(98.039246%, 82.745361%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.515625" stop-color="rgb(98.039246%, 82.745361%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.523438" stop-color="rgb(98.039246%, 82.35321%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.53125" stop-color="rgb(97.647095%, 81.96106%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.535156" stop-color="rgb(97.647095%, 81.96106%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.539063" stop-color="rgb(97.647095%, 81.568909%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.542969" stop-color="rgb(97.647095%, 81.176758%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.546875" stop-color="rgb(97.647095%, 81.176758%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.554688" stop-color="rgb(97.647095%, 81.176758%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.5625" stop-color="rgb(97.647095%, 80.784607%, 84.313965%)" stop-opacity="1"/>
|
||||
<stop offset="0.570312" stop-color="rgb(97.647095%, 80.392456%, 83.921814%)" stop-opacity="1"/>
|
||||
<stop offset="0.574219" stop-color="rgb(97.647095%, 80.000305%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.578125" stop-color="rgb(97.647095%, 80.000305%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.582031" stop-color="rgb(97.647095%, 79.608154%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.585938" stop-color="rgb(97.647095%, 79.608154%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.59375" stop-color="rgb(97.647095%, 79.608154%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.601562" stop-color="rgb(97.254944%, 79.216003%, 82.745361%)" stop-opacity="1"/>
|
||||
<stop offset="0.605469" stop-color="rgb(97.254944%, 78.823853%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.609375" stop-color="rgb(97.254944%, 78.431702%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.617187" stop-color="rgb(97.254944%, 78.431702%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.625" stop-color="rgb(97.254944%, 78.039551%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.632813" stop-color="rgb(97.254944%, 78.039551%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.636719" stop-color="rgb(97.254944%, 77.6474%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.640625" stop-color="rgb(97.254944%, 77.255249%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.644531" stop-color="rgb(97.254944%, 77.255249%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.648438" stop-color="rgb(97.254944%, 76.863098%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.65625" stop-color="rgb(97.254944%, 76.863098%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.664062" stop-color="rgb(96.862793%, 76.470947%, 80.784607%)" stop-opacity="1"/>
|
||||
<stop offset="0.671875" stop-color="rgb(96.862793%, 76.078796%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.675781" stop-color="rgb(96.862793%, 76.078796%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.679688" stop-color="rgb(96.862793%, 75.686646%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.683594" stop-color="rgb(96.862793%, 75.686646%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.6875" stop-color="rgb(96.862793%, 75.294495%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.695312" stop-color="rgb(96.862793%, 75.294495%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.703125" stop-color="rgb(96.862793%, 74.902344%, 79.608154%)" stop-opacity="1"/>
|
||||
<stop offset="0.710937" stop-color="rgb(96.862793%, 74.510193%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.71875" stop-color="rgb(96.862793%, 74.510193%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.726562" stop-color="rgb(96.862793%, 74.118042%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.730469" stop-color="rgb(96.862793%, 73.725891%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.734375" stop-color="rgb(96.470642%, 73.725891%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.742188" stop-color="rgb(96.470642%, 73.33374%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.75" stop-color="rgb(96.470642%, 72.941589%, 78.039551%)" stop-opacity="1"/>
|
||||
<stop offset="0.757812" stop-color="rgb(96.470642%, 72.941589%, 78.039551%)" stop-opacity="1"/>
|
||||
<stop offset="0.765625" stop-color="rgb(96.470642%, 72.549438%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.769531" stop-color="rgb(96.470642%, 72.157288%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.773438" stop-color="rgb(96.470642%, 72.157288%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.78125" stop-color="rgb(96.470642%, 71.765137%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.789063" stop-color="rgb(96.470642%, 71.372986%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="0.796875" stop-color="rgb(96.470642%, 71.372986%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="0.804688" stop-color="rgb(96.470642%, 70.980835%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.8125" stop-color="rgb(96.470642%, 70.980835%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.820312" stop-color="rgb(96.078491%, 70.588684%, 76.078796%)" stop-opacity="1"/>
|
||||
<stop offset="0.828125" stop-color="rgb(96.078491%, 70.196533%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="0.832031" stop-color="rgb(96.078491%, 69.804382%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="0.835938" stop-color="rgb(96.078491%, 69.804382%, 75.294495%)" stop-opacity="1"/>
|
||||
<stop offset="0.839844" stop-color="rgb(96.078491%, 69.412231%, 75.294495%)" stop-opacity="1"/>
|
||||
<stop offset="0.84375" stop-color="rgb(96.078491%, 69.412231%, 75.294495%)" stop-opacity="1"/>
|
||||
<stop offset="0.851562" stop-color="rgb(96.078491%, 69.412231%, 75.294495%)" stop-opacity="1"/>
|
||||
<stop offset="0.859375" stop-color="rgb(96.078491%, 69.020081%, 74.902344%)" stop-opacity="1"/>
|
||||
<stop offset="0.867187" stop-color="rgb(96.078491%, 68.62793%, 74.510193%)" stop-opacity="1"/>
|
||||
<stop offset="0.875" stop-color="rgb(96.078491%, 68.62793%, 74.510193%)" stop-opacity="1"/>
|
||||
<stop offset="0.882813" stop-color="rgb(96.078491%, 68.235779%, 74.118042%)" stop-opacity="1"/>
|
||||
<stop offset="0.890625" stop-color="rgb(96.078491%, 68.235779%, 74.118042%)" stop-opacity="1"/>
|
||||
<stop offset="0.898438" stop-color="rgb(96.078491%, 67.843628%, 74.118042%)" stop-opacity="1"/>
|
||||
<stop offset="0.90625" stop-color="rgb(95.68634%, 67.451477%, 73.725891%)" stop-opacity="1"/>
|
||||
<stop offset="0.9375" stop-color="rgb(95.68634%, 67.451477%, 73.725891%)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(95.68634%, 67.451477%, 73.725891%)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip-8">
|
||||
<path clip-rule="nonzero" d="M 42 44 L 120 44 L 120 81 L 42 81 Z M 42 44 "/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-9">
|
||||
<path clip-rule="nonzero" d="M 60.722656 44.191406 C 50.570312 44.191406 42.34375 52.421875 42.34375 62.570312 C 42.34375 72.71875 50.570312 80.949219 60.722656 80.949219 L 100.917969 80.949219 C 111.066406 80.949219 119.296875 72.71875 119.296875 62.570312 C 119.296875 52.421875 111.066406 44.191406 100.917969 44.191406 Z M 60.722656 44.191406 "/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-pattern-4" gradientUnits="userSpaceOnUse" x1="-0.00000221172" y1="0" x2="1.000001" y2="0" gradientTransform="matrix(76.95372, 0, 0, 76.95372, 42.34317, 62.570047)">
|
||||
<stop offset="0" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.0078125" stop-color="rgb(100%, 100%, 100%)" stop-opacity="1"/>
|
||||
<stop offset="0.015625" stop-color="rgb(99.607849%, 99.607849%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.0234375" stop-color="rgb(99.215698%, 99.607849%, 99.607849%)" stop-opacity="1"/>
|
||||
<stop offset="0.03125" stop-color="rgb(98.823547%, 99.215698%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.0390625" stop-color="rgb(98.431396%, 99.215698%, 99.215698%)" stop-opacity="1"/>
|
||||
<stop offset="0.046875" stop-color="rgb(98.039246%, 98.823547%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.0546875" stop-color="rgb(97.647095%, 98.823547%, 98.823547%)" stop-opacity="1"/>
|
||||
<stop offset="0.0625" stop-color="rgb(97.254944%, 98.431396%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.0703125" stop-color="rgb(96.862793%, 98.431396%, 98.431396%)" stop-opacity="1"/>
|
||||
<stop offset="0.078125" stop-color="rgb(96.470642%, 98.039246%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.0859375" stop-color="rgb(96.078491%, 98.039246%, 98.039246%)" stop-opacity="1"/>
|
||||
<stop offset="0.09375" stop-color="rgb(95.68634%, 98.039246%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.101562" stop-color="rgb(95.68634%, 98.039246%, 97.647095%)" stop-opacity="1"/>
|
||||
<stop offset="0.109375" stop-color="rgb(95.294189%, 97.647095%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.117187" stop-color="rgb(94.902039%, 97.647095%, 97.254944%)" stop-opacity="1"/>
|
||||
<stop offset="0.125" stop-color="rgb(94.509888%, 97.254944%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.132812" stop-color="rgb(94.117737%, 97.254944%, 96.862793%)" stop-opacity="1"/>
|
||||
<stop offset="0.140625" stop-color="rgb(93.725586%, 96.862793%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.148438" stop-color="rgb(93.333435%, 96.862793%, 96.470642%)" stop-opacity="1"/>
|
||||
<stop offset="0.15625" stop-color="rgb(92.941284%, 96.470642%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.164062" stop-color="rgb(92.549133%, 96.470642%, 96.078491%)" stop-opacity="1"/>
|
||||
<stop offset="0.171875" stop-color="rgb(92.156982%, 96.078491%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.179688" stop-color="rgb(91.764832%, 96.078491%, 95.68634%)" stop-opacity="1"/>
|
||||
<stop offset="0.1875" stop-color="rgb(91.372681%, 95.68634%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.195312" stop-color="rgb(90.98053%, 95.68634%, 95.294189%)" stop-opacity="1"/>
|
||||
<stop offset="0.203125" stop-color="rgb(90.588379%, 95.294189%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.210938" stop-color="rgb(90.196228%, 95.294189%, 94.902039%)" stop-opacity="1"/>
|
||||
<stop offset="0.21875" stop-color="rgb(89.804077%, 94.902039%, 94.509888%)" stop-opacity="1"/>
|
||||
<stop offset="0.226562" stop-color="rgb(89.411926%, 94.902039%, 94.117737%)" stop-opacity="1"/>
|
||||
<stop offset="0.234375" stop-color="rgb(89.019775%, 94.509888%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.242188" stop-color="rgb(88.627625%, 94.509888%, 93.725586%)" stop-opacity="1"/>
|
||||
<stop offset="0.25" stop-color="rgb(88.235474%, 94.117737%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.257812" stop-color="rgb(87.843323%, 94.117737%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.265625" stop-color="rgb(87.451172%, 94.117737%, 93.333435%)" stop-opacity="1"/>
|
||||
<stop offset="0.273438" stop-color="rgb(87.059021%, 93.725586%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.28125" stop-color="rgb(86.66687%, 93.725586%, 92.941284%)" stop-opacity="1"/>
|
||||
<stop offset="0.289062" stop-color="rgb(86.274719%, 93.333435%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.296875" stop-color="rgb(85.882568%, 93.333435%, 92.549133%)" stop-opacity="1"/>
|
||||
<stop offset="0.304687" stop-color="rgb(85.490417%, 92.941284%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.3125" stop-color="rgb(85.098267%, 92.941284%, 92.156982%)" stop-opacity="1"/>
|
||||
<stop offset="0.320312" stop-color="rgb(84.706116%, 92.549133%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.328125" stop-color="rgb(84.313965%, 92.549133%, 91.764832%)" stop-opacity="1"/>
|
||||
<stop offset="0.335937" stop-color="rgb(83.921814%, 92.156982%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.34375" stop-color="rgb(83.529663%, 92.156982%, 91.372681%)" stop-opacity="1"/>
|
||||
<stop offset="0.351562" stop-color="rgb(83.137512%, 92.156982%, 90.98053%)" stop-opacity="1"/>
|
||||
<stop offset="0.359375" stop-color="rgb(82.745361%, 92.156982%, 90.588379%)" stop-opacity="1"/>
|
||||
<stop offset="0.367187" stop-color="rgb(82.35321%, 91.764832%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.375" stop-color="rgb(81.96106%, 91.764832%, 90.196228%)" stop-opacity="1"/>
|
||||
<stop offset="0.382812" stop-color="rgb(81.568909%, 91.372681%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.390625" stop-color="rgb(81.176758%, 91.372681%, 89.804077%)" stop-opacity="1"/>
|
||||
<stop offset="0.398437" stop-color="rgb(80.784607%, 90.98053%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.40625" stop-color="rgb(80.392456%, 90.98053%, 89.411926%)" stop-opacity="1"/>
|
||||
<stop offset="0.414062" stop-color="rgb(80.000305%, 90.588379%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.421875" stop-color="rgb(79.608154%, 90.588379%, 89.019775%)" stop-opacity="1"/>
|
||||
<stop offset="0.429687" stop-color="rgb(79.216003%, 90.196228%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.4375" stop-color="rgb(78.823853%, 90.196228%, 88.627625%)" stop-opacity="1"/>
|
||||
<stop offset="0.445312" stop-color="rgb(78.431702%, 89.804077%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.453125" stop-color="rgb(78.039551%, 89.804077%, 88.235474%)" stop-opacity="1"/>
|
||||
<stop offset="0.460937" stop-color="rgb(77.6474%, 89.411926%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.46875" stop-color="rgb(77.255249%, 89.411926%, 87.843323%)" stop-opacity="1"/>
|
||||
<stop offset="0.476562" stop-color="rgb(76.863098%, 89.019775%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.484375" stop-color="rgb(76.470947%, 89.019775%, 87.451172%)" stop-opacity="1"/>
|
||||
<stop offset="0.492187" stop-color="rgb(76.078796%, 88.627625%, 87.059021%)" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="rgb(75.686646%, 88.627625%, 87.059021%)" stop-opacity="1"/>
|
||||
<stop offset="0.507812" stop-color="rgb(75.686646%, 88.627625%, 86.66687%)" stop-opacity="1"/>
|
||||
<stop offset="0.515625" stop-color="rgb(75.294495%, 88.627625%, 86.66687%)" stop-opacity="1"/>
|
||||
<stop offset="0.523437" stop-color="rgb(74.902344%, 88.235474%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.527344" stop-color="rgb(74.118042%, 88.235474%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.53125" stop-color="rgb(74.118042%, 88.235474%, 86.274719%)" stop-opacity="1"/>
|
||||
<stop offset="0.535156" stop-color="rgb(73.725891%, 87.843323%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.539062" stop-color="rgb(73.33374%, 87.843323%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.542969" stop-color="rgb(73.33374%, 87.843323%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.546875" stop-color="rgb(72.941589%, 87.843323%, 85.882568%)" stop-opacity="1"/>
|
||||
<stop offset="0.554687" stop-color="rgb(72.941589%, 87.451172%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.5625" stop-color="rgb(72.549438%, 87.451172%, 85.490417%)" stop-opacity="1"/>
|
||||
<stop offset="0.570312" stop-color="rgb(72.157288%, 87.059021%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.578125" stop-color="rgb(71.765137%, 87.059021%, 85.098267%)" stop-opacity="1"/>
|
||||
<stop offset="0.585937" stop-color="rgb(71.372986%, 86.66687%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.59375" stop-color="rgb(70.980835%, 86.66687%, 84.706116%)" stop-opacity="1"/>
|
||||
<stop offset="0.601562" stop-color="rgb(70.588684%, 86.274719%, 84.313965%)" stop-opacity="1"/>
|
||||
<stop offset="0.609375" stop-color="rgb(70.196533%, 86.274719%, 84.313965%)" stop-opacity="1"/>
|
||||
<stop offset="0.617187" stop-color="rgb(69.804382%, 85.882568%, 83.921814%)" stop-opacity="1"/>
|
||||
<stop offset="0.625" stop-color="rgb(69.412231%, 85.882568%, 83.921814%)" stop-opacity="1"/>
|
||||
<stop offset="0.632812" stop-color="rgb(69.020081%, 85.490417%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.636719" stop-color="rgb(68.62793%, 85.490417%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.640625" stop-color="rgb(68.235779%, 85.490417%, 83.529663%)" stop-opacity="1"/>
|
||||
<stop offset="0.644531" stop-color="rgb(67.843628%, 85.098267%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.648437" stop-color="rgb(67.451477%, 85.098267%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.652344" stop-color="rgb(67.451477%, 85.098267%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.65625" stop-color="rgb(67.059326%, 85.098267%, 83.137512%)" stop-opacity="1"/>
|
||||
<stop offset="0.664062" stop-color="rgb(67.059326%, 84.706116%, 82.745361%)" stop-opacity="1"/>
|
||||
<stop offset="0.671875" stop-color="rgb(66.667175%, 84.706116%, 82.745361%)" stop-opacity="1"/>
|
||||
<stop offset="0.679687" stop-color="rgb(66.275024%, 84.313965%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.6875" stop-color="rgb(65.882874%, 84.313965%, 82.35321%)" stop-opacity="1"/>
|
||||
<stop offset="0.695312" stop-color="rgb(65.490723%, 84.313965%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.703125" stop-color="rgb(65.098572%, 84.313965%, 81.96106%)" stop-opacity="1"/>
|
||||
<stop offset="0.710937" stop-color="rgb(64.706421%, 83.921814%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.71875" stop-color="rgb(64.31427%, 83.921814%, 81.568909%)" stop-opacity="1"/>
|
||||
<stop offset="0.726562" stop-color="rgb(63.922119%, 83.529663%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.734375" stop-color="rgb(63.529968%, 83.529663%, 81.176758%)" stop-opacity="1"/>
|
||||
<stop offset="0.742187" stop-color="rgb(63.137817%, 83.137512%, 80.784607%)" stop-opacity="1"/>
|
||||
<stop offset="0.746094" stop-color="rgb(62.745667%, 83.137512%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.75" stop-color="rgb(62.353516%, 83.137512%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.753906" stop-color="rgb(62.353516%, 83.137512%, 80.392456%)" stop-opacity="1"/>
|
||||
<stop offset="0.757812" stop-color="rgb(61.961365%, 82.745361%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.761719" stop-color="rgb(61.569214%, 82.745361%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.765625" stop-color="rgb(61.569214%, 82.745361%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.773437" stop-color="rgb(61.177063%, 82.745361%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.78125" stop-color="rgb(60.784912%, 82.35321%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.789062" stop-color="rgb(60.392761%, 82.35321%, 80.000305%)" stop-opacity="1"/>
|
||||
<stop offset="0.796875" stop-color="rgb(60.00061%, 82.35321%, 79.608154%)" stop-opacity="1"/>
|
||||
<stop offset="0.804687" stop-color="rgb(59.608459%, 82.35321%, 79.608154%)" stop-opacity="1"/>
|
||||
<stop offset="0.8125" stop-color="rgb(59.216309%, 81.96106%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.820312" stop-color="rgb(58.824158%, 81.96106%, 79.216003%)" stop-opacity="1"/>
|
||||
<stop offset="0.828125" stop-color="rgb(58.432007%, 81.568909%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.835937" stop-color="rgb(58.039856%, 81.568909%, 78.823853%)" stop-opacity="1"/>
|
||||
<stop offset="0.84375" stop-color="rgb(57.647705%, 81.176758%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.851562" stop-color="rgb(57.255554%, 81.176758%, 78.431702%)" stop-opacity="1"/>
|
||||
<stop offset="0.859375" stop-color="rgb(56.863403%, 80.784607%, 78.039551%)" stop-opacity="1"/>
|
||||
<stop offset="0.867187" stop-color="rgb(56.471252%, 80.784607%, 78.039551%)" stop-opacity="1"/>
|
||||
<stop offset="0.875" stop-color="rgb(56.079102%, 80.784607%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.882812" stop-color="rgb(55.686951%, 80.784607%, 77.6474%)" stop-opacity="1"/>
|
||||
<stop offset="0.890625" stop-color="rgb(55.2948%, 80.392456%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.898437" stop-color="rgb(54.902649%, 80.392456%, 77.255249%)" stop-opacity="1"/>
|
||||
<stop offset="0.90625" stop-color="rgb(54.510498%, 80.000305%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="0.914062" stop-color="rgb(54.118347%, 80.000305%, 76.863098%)" stop-opacity="1"/>
|
||||
<stop offset="0.921875" stop-color="rgb(53.726196%, 79.608154%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.929687" stop-color="rgb(53.334045%, 79.608154%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.9375" stop-color="rgb(52.941895%, 79.608154%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.945312" stop-color="rgb(52.549744%, 79.608154%, 76.470947%)" stop-opacity="1"/>
|
||||
<stop offset="0.953125" stop-color="rgb(52.157593%, 79.216003%, 76.078796%)" stop-opacity="1"/>
|
||||
<stop offset="0.960937" stop-color="rgb(51.765442%, 79.216003%, 76.078796%)" stop-opacity="1"/>
|
||||
<stop offset="0.96875" stop-color="rgb(51.373291%, 78.823853%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="0.976562" stop-color="rgb(51.373291%, 78.823853%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="0.984375" stop-color="rgb(50.98114%, 78.823853%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="0.992187" stop-color="rgb(50.588989%, 78.823853%, 75.686646%)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(50.196838%, 78.431702%, 75.294495%)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g clip-path="url(#clip-0)">
|
||||
<g clip-path="url(#clip-1)">
|
||||
<path fill-rule="nonzero" fill="url(#linear-pattern-0)" d="M 79.097656 80.949219 L 79.097656 44.191406 L 2.144531 44.191406 L 2.144531 80.949219 Z M 79.097656 80.949219 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g clip-path="url(#clip-2)">
|
||||
<g clip-path="url(#clip-3)">
|
||||
<path fill-rule="nonzero" fill="url(#linear-pattern-1)" d="M 46.507812 115.332031 L 113.484375 48.359375 L 46.507812 -18.617188 L -20.464844 48.359375 Z M 46.507812 115.332031 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g clip-path="url(#clip-4)">
|
||||
<g clip-path="url(#clip-5)">
|
||||
<path fill-rule="nonzero" fill="url(#linear-pattern-2)" d="M 42.34375 80.949219 L 79.097656 80.949219 L 79.097656 3.996094 L 42.34375 3.996094 Z M 42.34375 80.949219 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g clip-path="url(#clip-6)">
|
||||
<g clip-path="url(#clip-7)">
|
||||
<path fill-rule="nonzero" fill="url(#linear-pattern-3)" d="M 7.960938 48.359375 L 74.933594 115.332031 L 141.90625 48.359375 L 74.933594 -18.617188 Z M 7.960938 48.359375 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g clip-path="url(#clip-8)">
|
||||
<g clip-path="url(#clip-9)">
|
||||
<path fill-rule="nonzero" fill="url(#linear-pattern-4)" d="M 42.34375 44.191406 L 42.34375 80.949219 L 119.296875 80.949219 L 119.296875 44.191406 Z M 42.34375 44.191406 "/>
|
||||
</g>
|
||||
</g>
|
||||
<path fill-rule="nonzero" fill="rgb(100%, 100%, 100%)" fill-opacity="1" d="M 79.097656 62.570312 C 79.097656 72.71875 70.871094 80.949219 60.722656 80.949219 C 50.570312 80.949219 42.34375 72.71875 42.34375 62.570312 C 42.34375 52.421875 50.570312 44.191406 60.722656 44.191406 C 70.871094 44.191406 79.097656 52.421875 79.097656 62.570312 "/>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="133.1255" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-1" x="141.8111" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-2" x="158.2975" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-3" x="170.0435" y="39.8591"/>
|
||||
<use xlink:href="#glyph-0-4" x="187.3475" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-5" x="198.5587" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-6" x="224.2543" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="241.4435" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-8" x="253.7943" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-9" x="261.5951" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-10" x="279.0391" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-11" x="295.5983" y="39.8591"/>
|
||||
<use xlink:href="#glyph-0-12" x="312.1183" y="39.8591"/>
|
||||
<use xlink:href="#glyph-0-11" x="316.5983" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="332.8551" y="39.8591"/>
|
||||
<use xlink:href="#glyph-0-12" x="344.9791" y="39.8591"/>
|
||||
<use xlink:href="#glyph-0-13" x="349.4591" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-14" x="362.3475" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-13" x="378.3775" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="391.1511" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-15" x="403.2219" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-5" x="419.8231" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-11" x="445.4655" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-13" x="462.0303" y="39.8591"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-16" x="133.1255" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-11" x="150.4547" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-12" x="166.9747" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-17" x="171.4547" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-3" x="185.8383" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-5" x="203.1423" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-5" x="228.9331" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-10" x="254.6539" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-1" x="271.2971" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-8" x="288.0467" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-17" x="295.7887" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-6" x="310.2927" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="327.4847" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-8" x="339.8327" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-3" x="347.5747" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-1" x="364.8787" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-12" x="381.5107" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-12" x="385.9907" y="66.8511"/>
|
||||
<use xlink:href="#glyph-0-0" x="390.4707" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-18" x="399.0331" y="66.8511"/>
|
||||
</g>
|
||||
<g fill="rgb(21.960449%, 21.960449%, 21.960449%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-19" x="415.7099" y="66.8511"/>
|
||||
</g>
|
||||
<path fill-rule="nonzero" fill="rgb(86.66687%, 0%, 41.175842%)" fill-opacity="1" d="M 453.445312 65.542969 C 453.445312 66.425781 454.160156 67.140625 455.039062 67.140625 L 472.71875 67.140625 C 473.597656 67.140625 474.3125 66.425781 474.3125 65.542969 L 474.3125 47.867188 C 474.3125 46.984375 473.597656 46.273438 472.71875 46.273438 L 455.039062 46.273438 C 454.160156 46.273438 453.445312 46.984375 453.445312 47.867188 Z M 453.445312 65.542969 "/>
|
||||
<path fill-rule="nonzero" fill="rgb(100%, 100%, 100%)" fill-opacity="1" d="M 465.027344 52.910156 L 462.621094 52.910156 C 462.5 54.304688 462.382812 55.492188 462.273438 56.386719 C 462.160156 57.277344 462.054688 57.957031 461.953125 58.597656 C 461.84375 59.242188 461.738281 59.824219 461.628906 60.351562 C 461.535156 60.765625 461.40625 61.085938 461.226562 61.316406 C 461.050781 61.550781 460.796875 61.710938 460.460938 61.800781 C 460.175781 61.867188 459.902344 61.816406 459.625 61.632812 C 459.351562 61.453125 459.191406 61.238281 459.128906 60.988281 C 459.089844 60.800781 459.078125 60.644531 459.101562 60.515625 C 459.117188 60.386719 459.15625 60.265625 459.214844 60.15625 C 459.269531 60.046875 459.332031 59.945312 459.40625 59.851562 C 459.46875 59.761719 459.542969 59.671875 459.617188 59.582031 C 459.644531 59.554688 459.761719 59.414062 459.984375 59.15625 C 460.203125 58.898438 460.363281 58.664062 460.476562 58.453125 C 460.585938 58.242188 460.828125 57.773438 460.949219 57.195312 C 461.070312 56.617188 461.320312 54.632812 461.316406 52.910156 L 460.300781 52.910156 C 460.015625 52.910156 459.734375 53.003906 459.472656 53.140625 C 459.21875 53.273438 458.992188 53.5 458.890625 53.632812 C 458.78125 53.765625 458.503906 54.210938 458.441406 54.296875 C 458.375 54.382812 458.242188 54.582031 458.144531 54.550781 C 458.035156 54.515625 458.058594 54.394531 458.074219 54.296875 C 458.09375 54.199219 458.292969 53.699219 458.441406 53.324219 C 458.589844 52.945312 458.792969 52.578125 459.046875 52.226562 C 459.246094 51.925781 459.519531 51.710938 459.859375 51.496094 C 460.203125 51.273438 460.597656 51.164062 461.050781 51.164062 L 468.429688 51.164062 C 468.5625 51.164062 469.253906 51.21875 469.253906 52.03125 C 469.253906 52.839844 468.515625 52.910156 468.429688 52.910156 L 466.335938 52.910156 C 466.160156 54.570312 466.074219 56.28125 466.074219 57.964844 C 466.074219 58.269531 466.125 58.570312 466.230469 58.859375 C 466.335938 59.152344 466.464844 59.375 466.617188 59.523438 C 466.816406 59.707031 467.050781 59.824219 467.332031 59.875 C 467.617188 59.925781 467.902344 59.839844 468.191406 59.609375 C 468.351562 59.484375 468.46875 59.34375 468.542969 59.1875 C 468.617188 59.023438 468.660156 58.871094 468.683594 58.726562 C 468.703125 58.578125 468.738281 58.371094 468.742188 58.320312 C 468.746094 58.273438 468.738281 58.089844 468.945312 58.074219 C 469.152344 58.058594 469.171875 58.242188 469.191406 58.441406 C 469.210938 58.640625 469.101562 59.382812 468.9375 59.84375 C 468.769531 60.308594 468.570312 60.679688 468.339844 60.957031 C 468.109375 61.242188 467.886719 61.445312 467.664062 61.566406 C 467.410156 61.71875 467.117188 61.8125 466.785156 61.84375 C 466.449219 61.878906 466.140625 61.796875 465.867188 61.613281 C 465.40625 61.277344 465.054688 60.753906 464.824219 60.039062 C 464.683594 59.621094 464.617188 59.070312 464.597656 58.386719 C 464.578125 57.703125 464.601562 57.035156 464.664062 56.382812 Z M 465.027344 52.910156 "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 96 KiB |
@@ -0,0 +1,13 @@
|
||||
Info in /tmp/test3.py from L1:15 to L1:17:
|
||||
mean_height = [36mdf[0m["height"].mean()
|
||||
[36m~~> Type: Frame[name: Column[str], height: Column[Meter], age: Column[Age]][0m
|
||||
|
||||
Info in /tmp/test3.py from L1:15 to L1:27:
|
||||
mean_height = [36mdf["height"][0m.mean()
|
||||
[36m~~~~~~~~~~~~> Type: Column[Meter][0m
|
||||
|
||||
Info in /tmp/test3.py from L1:15 to L1:34:
|
||||
mean_height = [36mdf["height"].mean()[0m
|
||||
[36m~~~~~~~~~~~~~~~~~~~> Type: Meter[0m
|
||||
|
||||
[36mInfos[0m: 3
|
||||
@@ -0,0 +1,5 @@
|
||||
Error in /tmp/script.py from L2:12 to L2:15:
|
||||
fib.append([31m3.0[0m)
|
||||
[31m~~~> Wrong type for argument 'object', expected int, got float[0m
|
||||
|
||||
[31mErrors[0m: 1
|
||||
@@ -0,0 +1,33 @@
|
||||
Info in /tmp/script.py from L1:8 to L1:9:
|
||||
fib = [[36m1[0m,1,2]
|
||||
[36m~> Type: int[0m
|
||||
|
||||
Info in /tmp/script.py from L1:10 to L1:11:
|
||||
fib = [1,[36m1[0m,2]
|
||||
[36m~> Type: int[0m
|
||||
|
||||
Info in /tmp/script.py from L1:12 to L1:13:
|
||||
fib = [1,1,[36m2[0m]
|
||||
[36m~> Type: int[0m
|
||||
|
||||
Info in /tmp/script.py from L1:7 to L1:14:
|
||||
fib = [36m[1,1,2][0m
|
||||
[36m~~~~~~~> Type: list[int][0m
|
||||
|
||||
Info in /tmp/script.py from L2:12 to L2:15:
|
||||
fib.append([36m3.0[0m)
|
||||
[36m~~~> Type: float[0m
|
||||
|
||||
Info in /tmp/script.py from L2:1 to L2:4:
|
||||
[36mfib[0m.append(3.0)
|
||||
[36m~~~> Type: list[int][0m
|
||||
|
||||
Info in /tmp/script.py from L2:1 to L2:16:
|
||||
[36mfib.append(3.0)[0m
|
||||
[36m~~~~~~~~~~~~~~~> Type: <Unknown>[0m
|
||||
|
||||
Error in /tmp/script.py from L2:12 to L2:15:
|
||||
fib.append([31m3.0[0m)
|
||||
[31m~~~> Wrong type for argument 'object', expected int, got float[0m
|
||||
|
||||
[31mErrors[0m: 1 [36mInfos[0m: 7
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
height="175.94801"
|
||||
width="873.66284"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mypy_light.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="1001"
|
||||
id="namedview4260"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0737884"
|
||||
inkscape:cx="473.10057"
|
||||
inkscape:cy="55.723359"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<defs
|
||||
id="defs4" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8615"
|
||||
d="m 87.796991,0 c -44.90034,0 -42.09648,19.4715 -42.09648,19.4715 l 0.05,20.17227 42.8473,0 0,6.05668 -59.866091,0 c 0,0 -28.73172,-3.25844 -28.73172,42.04643 0,45.30485 25.07769,43.69824 25.07769,43.69824 l 14.966521,0 0,-21.02321 c 0,0 -0.80673,-25.07769 24.67724,-25.07769 25.48398,0 42.496919,0 42.496919,0 0,0 23.87636,0.38596 23.87636,-23.07547 0,-23.46144 0,-38.79283 0,-38.79283 0,0 3.62509,-23.47592 -43.297799,-23.47592 z m -23.62608,13.56498 c 4.26299,0 7.70851,3.44551 7.70851,7.70851 0,4.26299 -3.44552,7.70851 -7.70851,7.70851 -4.263,0 -7.70851,-3.44552 -7.70851,-7.70851 0,-4.263 3.44551,-7.70851 7.70851,-7.70851 z"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#2a6db2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none"
|
||||
d="m 89.072161,175.94801 c 44.900349,0 42.096469,-19.47149 42.096469,-19.47149 l -0.05,-20.17228 -42.847309,0 0,-6.05668 59.866099,0 c 0,0 28.73172,3.25843 28.73172,-42.04644 2e-5,-45.30485 -25.07769,-43.69824 -25.07769,-43.69824 l -14.96652,0 0,21.02321 c 0,0 0.80675,25.07768 -24.67725,25.07768 -25.483969,0 -42.496909,0 -42.496909,0 0,0 -23.87636,-0.38596 -23.87636,23.0755 0,23.46142 0,38.79282 0,38.79282 0,0 -3.62509,23.47592 43.2978,23.47592 z m 23.626079,-13.56497 c -4.26299,0 -7.70851,-3.44552 -7.70851,-7.70851 0,-4.263 3.44552,-7.70851 7.70851,-7.70851 4.263,0 7.70851,3.44551 7.70851,7.70851 2e-5,4.26299 -3.44551,7.70851 -7.70851,7.70851 z"
|
||||
id="path8620" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3890"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#505050;fill-opacity:1;stroke:none"
|
||||
d="m 211.33512,110.31397 16.94571,0 0,20.39908 -16.94571,0 0,-20.39908 m 0,-64.65064 16.94571,0 0,20.39908 -16.94571,0 0,-20.39908" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3892"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#2a6db2;fill-opacity:1;stroke:none"
|
||||
d="m 385.53045,56.03127 c 3.69422,-6.63898 7.57434,-9.86018 12.71435,-13.01919 5.13983,-3.15881 11.72695,-4.41606 18.68739,-4.41616 9.36953,1e-4 16.59754,3.29285 21.68407,9.8783 5.08624,6.53208 7.62943,15.84819 7.62957,27.94836 l 0,54.29047 -14.8576,0 0,-53.80861 c -1.3e-4,-8.62001 -1.52605,-15.01815 -4.57774,-19.19441 -3.05196,-4.17611 -7.71002,-6.26421 -13.97418,-6.26429 -7.65645,8e-5 -13.70657,2.54327 -18.15038,7.62958 -4.44397,5.08646 -6.66592,12.02 -6.66584,20.80064 l 0,50.83709 -14.8576,0 0,-53.80861 c -6e-5,-8.67356 -1.52599,-15.07169 -4.57774,-19.19441 -3.0519,-4.17611 -7.7635,-6.26421 -14.13481,-6.26429 -7.54931,8e-5 -13.54588,2.57004 -17.98973,7.70989 -4.44393,5.08646 -6.66587,11.99322 -6.66584,20.72033 l 0,50.83709 -14.8576,0 0,-89.94872 14.8576,0 0,11.97418 c 3.37304,-5.51463 6.87835,-8.01335 11.58999,-10.63695 4.71155,-2.62341 10.8436,-3.50554 17.32209,-3.50564 6.53193,1e-4 12.07341,1.65987 16.62445,4.97931 4.60445,3.31962 8.00428,6.1383 10.19955,12.45604" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3894"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#2a6db2;fill-opacity:1;stroke:none"
|
||||
d="m 513.22549,139.06544 c -4.17622,10.70815 -8.24534,17.69524 -12.20731,20.96125 -3.96207,3.26597 -9.26261,4.89896 -15.90166,4.89899 l -11.80577,0 0,-12.36794 8.67363,0 c 4.06908,-2e-5 7.22799,-0.96375 9.47675,-2.89121 2.24867,-1.92749 4.73833,-6.47846 7.46895,-13.65293 l 2.65027,-6.74615 -36.38105,-88.50312 15.66072,0 28.10898,70.35275 28.10897,-70.35275 15.66072,0 -39.5132,98.30111" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3896"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#505050;fill-opacity:1;stroke:none"
|
||||
d="m 571.77248,5.74859 34.05203,0 0,11.48452 -19.27474,0 0,123.67949 19.27474,0 0,11.48452 -34.05203,0 0,-146.64853" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3898"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#2a6db2;fill-opacity:1;stroke:none"
|
||||
d="m 651.68231,117.22074 0,47.70494 -14.85761,0 0,-124.16135 14.85761,0 0,13.65293 c 3.10535,-5.354 7.01382,-9.31603 11.72545,-11.88608 4.7651,-2.62341 10.44043,-3.93516 17.02602,-3.93526 10.92226,1e-4 19.78328,4.3369 26.58306,13.01045 6.85313,8.6737 10.27975,20.07789 10.27984,34.21263 -9e-5,14.13484 -3.42671,25.53903 -10.27984,34.21264 -6.79978,8.67364 -15.6608,13.01044 -26.58306,13.01044 -6.58559,0 -12.26092,-1.28498 -17.02602,-3.85494 -4.71163,-2.62351 -8.6201,-6.61229 -11.72545,-11.9664 m 45.52876,-56.94075 c -4.44396,-6.21066 -10.5744,-9.31603 -18.39131,-9.31611 -7.81702,8e-5 -13.97422,3.10545 -18.47161,9.31611 -4.44392,6.15728 -6.66588,14.67026 -6.66584,25.53901 -4e-5,10.86883 2.22192,19.40861 6.66584,25.61933 4.49739,6.15721 10.65459,9.23581 18.47161,9.23581 7.81691,0 13.94735,-3.0786 18.39131,-9.23581 4.49735,-6.21072 6.74615,-14.7505 6.74615,-25.61933 0,-10.86875 -2.2488,-19.38173 -6.74615,-25.53901 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3900"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#2a6db2;fill-opacity:1;stroke:none"
|
||||
d="m 779.21669,139.06544 c -4.17622,10.70815 -8.24534,17.69524 -12.20731,20.96125 -3.96207,3.26597 -9.26261,4.89896 -15.90166,4.89899 l -11.80577,0 0,-12.36794 8.67363,0 c 4.06908,-2e-5 7.22799,-0.96375 9.47675,-2.89121 2.24867,-1.92749 4.73832,-6.47846 7.46895,-13.65293 l 2.65027,-6.74615 -36.38105,-88.50312 15.66072,0 28.10898,70.35275 28.10897,-70.35275 15.66071,0 -39.51319,98.30111" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3902"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:102.68504333px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#505050;fill-opacity:1;stroke:none"
|
||||
d="m 873.66285,5.74859 0,146.64853 -34.052,0 0,-11.48452 19.1944,0 0,-123.67949 -19.1944,0 0,-11.48452 34.052,0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,243 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
version="1.0"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="1.4.4 (1:1.4.4+202605061436+dcaf3e7d9e)"
|
||||
sodipodi:docname="python-logo.svg"
|
||||
width="83.371017pt"
|
||||
height="84.291748pt"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata371">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-width="2584"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
inkscape:zoom="2.1461642"
|
||||
inkscape:cx="245.55437"
|
||||
inkscape:cy="61.039132"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="24"
|
||||
inkscape:current-layer="svg2"
|
||||
width="210mm"
|
||||
height="40mm"
|
||||
units="mm"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="pt"
|
||||
inkscape:window-maximized="1" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient2795">
|
||||
<stop
|
||||
style="stop-color:#b8b8b8;stop-opacity:0.49803922;"
|
||||
offset="0"
|
||||
id="stop2797" />
|
||||
<stop
|
||||
style="stop-color:#7f7f7f;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2799" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2787">
|
||||
<stop
|
||||
style="stop-color:#7f7f7f;stop-opacity:0.5;"
|
||||
offset="0"
|
||||
id="stop2789" />
|
||||
<stop
|
||||
style="stop-color:#7f7f7f;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2791" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3676">
|
||||
<stop
|
||||
style="stop-color:#b2b2b2;stop-opacity:0.5;"
|
||||
offset="0"
|
||||
id="stop3678" />
|
||||
<stop
|
||||
style="stop-color:#b3b3b3;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3680" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3236">
|
||||
<stop
|
||||
style="stop-color:#f4f4f4;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3244" />
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3240" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4671">
|
||||
<stop
|
||||
style="stop-color:#ffd43b;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4673" />
|
||||
<stop
|
||||
style="stop-color:#ffe873;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4675" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4689">
|
||||
<stop
|
||||
style="stop-color:#5a9fd4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4691" />
|
||||
<stop
|
||||
style="stop-color:#306998;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4693" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="224.23996"
|
||||
y1="144.75717"
|
||||
x2="-65.308502"
|
||||
y2="144.75717"
|
||||
id="linearGradient2987"
|
||||
xlink:href="#linearGradient4671"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(100.2702,99.61116)" />
|
||||
<linearGradient
|
||||
x1="172.94208"
|
||||
y1="77.475983"
|
||||
x2="26.670298"
|
||||
y2="76.313133"
|
||||
id="linearGradient2990"
|
||||
xlink:href="#linearGradient4689"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(100.2702,99.61116)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4689"
|
||||
id="linearGradient2587"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(100.2702,99.61116)"
|
||||
x1="172.94208"
|
||||
y1="77.475983"
|
||||
x2="26.670298"
|
||||
y2="76.313133" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4671"
|
||||
id="linearGradient2589"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(100.2702,99.61116)"
|
||||
x1="224.23996"
|
||||
y1="144.75717"
|
||||
x2="-65.308502"
|
||||
y2="144.75717" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4689"
|
||||
id="linearGradient2248"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(100.2702,99.61116)"
|
||||
x1="172.94208"
|
||||
y1="77.475983"
|
||||
x2="26.670298"
|
||||
y2="76.313133" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4671"
|
||||
id="linearGradient2250"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(100.2702,99.61116)"
|
||||
x1="224.23996"
|
||||
y1="144.75717"
|
||||
x2="-65.308502"
|
||||
y2="144.75717" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4671"
|
||||
id="linearGradient2255"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.562541,0,0,0.567972,-11.5974,-7.60954)"
|
||||
x1="224.23996"
|
||||
y1="144.75717"
|
||||
x2="-65.308502"
|
||||
y2="144.75717" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4689"
|
||||
id="linearGradient2258"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.562541,0,0,0.567972,-11.5974,-7.60954)"
|
||||
x1="172.94208"
|
||||
y1="76.176224"
|
||||
x2="26.670298"
|
||||
y2="76.313133" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2795"
|
||||
id="radialGradient2801"
|
||||
cx="61.518883"
|
||||
cy="132.28575"
|
||||
fx="61.518883"
|
||||
fy="132.28575"
|
||||
r="29.036913"
|
||||
gradientTransform="matrix(1,0,0,0.177966,0,108.7434)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4671"
|
||||
id="linearGradient1475"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.562541,0,0,0.567972,-14.99112,-11.702371)"
|
||||
x1="150.96111"
|
||||
y1="192.35176"
|
||||
x2="112.03144"
|
||||
y2="137.27299" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4689"
|
||||
id="linearGradient1478"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.562541,0,0,0.567972,-14.99112,-11.702371)"
|
||||
x1="26.648937"
|
||||
y1="20.603781"
|
||||
x2="135.66525"
|
||||
y2="114.39767" />
|
||||
</defs>
|
||||
<path
|
||||
style="fill:url(#linearGradient1478);fill-opacity:1"
|
||||
d="M 54.918785,9.1927389e-4 C 50.335132,0.02221727 45.957846,0.41313697 42.106285,1.0946693 30.760069,3.0991731 28.700036,7.2947714 28.700035,15.032169 v 10.21875 h 26.8125 v 3.40625 h -26.8125 -10.0625 c -7.792459,0 -14.6157588,4.683717 -16.7499998,13.59375 -2.46181998,10.212966 -2.57101508,16.586023 0,27.25 1.9059283,7.937852 6.4575432,13.593748 14.2499998,13.59375 h 9.21875 v -12.25 c 0,-8.849902 7.657144,-16.656248 16.75,-16.65625 h 26.78125 c 7.454951,0 13.406253,-6.138164 13.40625,-13.625 v -25.53125 c 0,-7.2663386 -6.12998,-12.7247771 -13.40625,-13.9374997 C 64.281548,0.32794397 59.502438,-0.02037903 54.918785,9.1927389e-4 Z m -14.5,8.21875012611 c 2.769547,0 5.03125,2.2986456 5.03125,5.1249996 -2e-6,2.816336 -2.261703,5.09375 -5.03125,5.09375 -2.779476,-1e-6 -5.03125,-2.277415 -5.03125,-5.09375 -10e-7,-2.826353 2.251774,-5.1249996 5.03125,-5.1249996 z"
|
||||
id="path1948" />
|
||||
<path
|
||||
style="fill:url(#linearGradient1475);fill-opacity:1"
|
||||
d="m 85.637535,28.657169 v 11.90625 c 0,9.230755 -7.825895,16.999999 -16.75,17 h -26.78125 c -7.335833,0 -13.406249,6.278483 -13.40625,13.625 v 25.531247 c 0,7.266344 6.318588,11.540324 13.40625,13.625004 8.487331,2.49561 16.626237,2.94663 26.78125,0 6.750155,-1.95439 13.406253,-5.88761 13.40625,-13.625004 V 86.500919 h -26.78125 v -3.40625 h 26.78125 13.406254 c 7.792461,0 10.696251,-5.435408 13.406241,-13.59375 2.79933,-8.398886 2.68022,-16.475776 0,-27.25 -1.92578,-7.757441 -5.60387,-13.59375 -13.406241,-13.59375 z m -15.0625,64.65625 c 2.779478,3e-6 5.03125,2.277417 5.03125,5.093747 -2e-6,2.826354 -2.251775,5.125004 -5.03125,5.125004 -2.76955,0 -5.03125,-2.29865 -5.03125,-5.125004 2e-6,-2.81633 2.261697,-5.093747 5.03125,-5.093747 z"
|
||||
id="path1950" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
@@ -0,0 +1,5 @@
|
||||
Traceback (most recent call last):
|
||||
File [35m"script.py"[0m, line [35m31[0m, in [35m<module>[0m
|
||||
assert [31misinstance[0m[1;31m(value, float)[0m, f"script.py:L2:6: CastError: Cannot cast {type(value).__name__} to float, in column 'age'"
|
||||
[31m~~~~~~~~~~[0m[1;31m^^^^^^^^^^^^^^[0m
|
||||
[1;35mAssertionError[0m: [35mscript.py:L2:6: CastError: Cannot cast int to float, in column 'age'[0m
|
||||
@@ -0,0 +1,5 @@
|
||||
Traceback (most recent call last):
|
||||
File [35m"script.py"[0m, line [35m32[0m, in [35m<module>[0m
|
||||
assert [31m__midas_p0__[0m[1;31m(value)[0m, "script.py:L2:6: ConstraintError: Value does not fit constraint 'in_range(0.0, 150.0)(_)', in column 'age'"
|
||||
[31m~~~~~~~~~~~~[0m[1;31m^^^^^^^[0m
|
||||
[1;35mAssertionError[0m: [35mscript.py:L2:6: ConstraintError: Value does not fit constraint 'in_range(0.0, 150.0)(_)', in column 'age'[0m
|
||||
@@ -0,0 +1,2 @@
|
||||
fib = [1, 1, 2]
|
||||
fib.append(3.0)
|
||||
@@ -0,0 +1,93 @@
|
||||
#import "requirements.typ": *
|
||||
#import "../meta.typ"
|
||||
#import codly: codly-init, codly
|
||||
#import "@preview/codly-languages:0.1.10": codly-languages
|
||||
#import "@preview/tiaoma:0.3.0"
|
||||
|
||||
#pdfpc.config(
|
||||
start-time: datetime(hour: 13, minute: 15, second: 0),
|
||||
duration-minutes: 20,
|
||||
last-minutes: 3
|
||||
)
|
||||
|
||||
#show raw.where(block: true): set text(size: 0.8em)
|
||||
#show: codly-init.with()
|
||||
#codly(
|
||||
languages: codly-languages
|
||||
+ (
|
||||
midas: (
|
||||
name: "Midas",
|
||||
color: rgb("#eedd47"),
|
||||
icon: box(
|
||||
image(
|
||||
"../assets/icon.svg",
|
||||
height: 130%,
|
||||
fit: "contain",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#set raw(
|
||||
syntaxes: path("../midas.sublime-syntax")
|
||||
)
|
||||
|
||||
#show: unistra-theme.with(
|
||||
config-info(
|
||||
author: meta.authors,
|
||||
institution: meta.school,
|
||||
date: datetime(year: 2026, month: 8, day: 24).display("[day].[month].[year]"),
|
||||
title: [Midas],
|
||||
subtitle: [Hybrid Type Checking for Python],
|
||||
//title: meta.title,
|
||||
//subtitle: meta.subtitle,
|
||||
//short-title: [Midas],
|
||||
//short-subtitle: [Hybrid Type Checking for Python],
|
||||
//logo: image("figs/isc_logo.svg"),
|
||||
logo: {
|
||||
image("figs/isc_logo.svg")
|
||||
place(
|
||||
left + horizon,
|
||||
dx: 100% + 1em,
|
||||
clearance: 0pt,
|
||||
//scale(80%, tiaoma.qrcode("https://url.kb28.ch/nws0"))
|
||||
scale(80%, tiaoma.qrcode("https://git.kb28.ch/HEL/TB-Docs/releases/download/official/presentation_handout.pdf"))
|
||||
)
|
||||
},
|
||||
),
|
||||
config-common(
|
||||
enable-pdfpc: true,
|
||||
handout: sys.inputs.at("handout", default: "false") == "true",
|
||||
slide-fn: slide,
|
||||
),
|
||||
config-store(
|
||||
footer-appendix-label: "A",
|
||||
show-header: {
|
||||
place(
|
||||
top + left,
|
||||
utils.touying-progress(
|
||||
ratio => box(
|
||||
width: ratio * 100%,
|
||||
height: 6pt,
|
||||
fill: orange.C
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
#title-slide(
|
||||
logos: (
|
||||
box(inset: (x: 0.8em), image("figs/hei_logo.svg", height: 2cm)),
|
||||
box(inset: (x: 0.8em), image("figs/isc_logo.svg", height: 2cm)),
|
||||
)
|
||||
)
|
||||
|
||||
#include "sections/01_introduction.typ"
|
||||
#include "sections/02_state_of_the_art.typ"
|
||||
#include "sections/03_midas.typ"
|
||||
#include "sections/04_status_and_future.typ"
|
||||
#include "sections/05_conclusion.typ"
|
||||
#include "sections/06_appendix.typ"
|
||||
@@ -0,0 +1,114 @@
|
||||
#import "@preview/touying:0.7.4": *
|
||||
#import "@preview/touying-unistra-pristine:1.4.3": *
|
||||
#import "@local/codly:1.3.1"
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/spidersouris/touying-unistra-pristine/blob/67084e00529991bfbd12152c0f074c582cba0093/src/unistra.typ#L22-L126
|
||||
#let slide(
|
||||
config: (:),
|
||||
repeat: auto,
|
||||
setting: body => body,
|
||||
composer: auto,
|
||||
..bodies,
|
||||
) = touying-slide-wrapper(self => {
|
||||
let footer(self) = {
|
||||
let cell(body) = rect(
|
||||
width: 100%,
|
||||
height: 100%,
|
||||
inset: 0mm,
|
||||
outset: 0mm,
|
||||
fill: none,
|
||||
stroke: none,
|
||||
text(size: 0.5em, fill: self.colors.black, body),
|
||||
)
|
||||
|
||||
let author = self.info.author
|
||||
let date = self.info.date
|
||||
if self.store.footer-hide.contains("author") {
|
||||
author = none
|
||||
}
|
||||
|
||||
if self.store.footer-hide.contains("date") {
|
||||
date = none
|
||||
}
|
||||
|
||||
set align(center + horizon)
|
||||
block(width: 101%, height: -25%, stroke: (top: 0.5pt + self.colors.black), {
|
||||
set text(size: 1.5em)
|
||||
|
||||
// give priority to short, since long is also used in title slide
|
||||
let title = self.info.title
|
||||
if (self.info.short-title != auto) {
|
||||
title = self.info.short-title
|
||||
}
|
||||
|
||||
let first-col-width = 15%
|
||||
let second-col-width = 75%
|
||||
|
||||
grid(
|
||||
columns: (first-col-width, second-col-width, 10%),
|
||||
rows: 0.5em,
|
||||
stroke: (x: 1pt + self.colors.black),
|
||||
cell(box(self.info.logo, height: 100%)),
|
||||
cell(box(
|
||||
width: 100%,
|
||||
text(
|
||||
title, // either title or short-title
|
||||
weight: "bold",
|
||||
)
|
||||
+ self.store.footer-first-sep
|
||||
+ if _is(author) { author }
|
||||
+ if _is(date) and _is(author) {
|
||||
self.store.footer-second-sep
|
||||
} else { "" }
|
||||
+ if _is(date) { date },
|
||||
)),
|
||||
cell(utils.call-or-display(self, context {
|
||||
let current = int(utils.slide-counter.display())
|
||||
let last = int(utils.last-slide-counter.display())
|
||||
if current > last {
|
||||
(
|
||||
text(self.store.footer-appendix-label, style: "italic")
|
||||
+ str(current)
|
||||
)
|
||||
} else {
|
||||
str(current)
|
||||
}
|
||||
}
|
||||
+ " / "
|
||||
+ utils.last-slide-number)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
let store-header = self.store.show-header
|
||||
let store-footer = self.store.show-footer
|
||||
|
||||
let self = utils.merge-dicts(self, config-page(
|
||||
header: if store-header == true {
|
||||
unistra-nav-bar(self)
|
||||
} else if store-header == false {
|
||||
none
|
||||
} else {
|
||||
store-header
|
||||
},
|
||||
footer: if store-footer == true {
|
||||
footer
|
||||
} else if store-footer == false {
|
||||
none
|
||||
} else {
|
||||
store-footer
|
||||
},
|
||||
// todo: change if no footer/header, etc.
|
||||
margin: (x: 3em, y: 2em),
|
||||
))
|
||||
|
||||
touying-slide(
|
||||
self: self,
|
||||
config: config,
|
||||
repeat: repeat,
|
||||
setting: setting,
|
||||
composer: composer,
|
||||
..bodies,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,95 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#focus-slide(theme: "berry")[The Issue]
|
||||
= The issue
|
||||
|
||||
== A popular language
|
||||
|
||||
#align(center, image("../figs/python-logo.svg"))
|
||||
|
||||
Python is a widely used language, especially in data science:
|
||||
#item-by-item[
|
||||
|
||||
- Easy to learn
|
||||
|
||||
- Simple syntax
|
||||
|
||||
- Vast and thriving ecosystem
|
||||
]
|
||||
|
||||
== It can bite
|
||||
|
||||
Everything is fine...
|
||||
|
||||
```python
|
||||
transactions = load_transactions()
|
||||
mean_amount = np.mean(transactions)
|
||||
```
|
||||
|
||||
#speaker-note[
|
||||
Il y a comme un couac -> duck-typing
|
||||
]
|
||||
|
||||
#pause
|
||||
|
||||
Until it's not
|
||||
|
||||
```python
|
||||
def load_transactions():
|
||||
return [
|
||||
usd_transaction,
|
||||
chf_transaction
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#{
|
||||
set text(size: 0.4em)
|
||||
table(
|
||||
columns: (1fr, 2fr, 0.5fr, 3.5fr),
|
||||
table.header[*Height*][*Country*][*Age*][*Profession*],
|
||||
// range(5 * 10).map(_ => line())
|
||||
..range(4 * 28).map(
|
||||
i => line(
|
||||
start: (0%, 0%),
|
||||
end: (100%, 0%),
|
||||
stroke: (
|
||||
dash: (
|
||||
array: (6pt, 3pt, "dot", 3pt),
|
||||
phase: calc.rem(i * 13, 31) * 1pt
|
||||
),
|
||||
paint: gray
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
pause
|
||||
|
||||
place(
|
||||
center + horizon,
|
||||
dx: -2.5em,
|
||||
box(
|
||||
stroke: 1pt,
|
||||
radius: 4pt,
|
||||
width: 8em,
|
||||
height: 6em,
|
||||
clip: true,
|
||||
fill: white,
|
||||
move(
|
||||
dx: -5em,
|
||||
dy: -1em,
|
||||
table(
|
||||
columns: (6em, 6em, 6em),
|
||||
rows: 1.5em,
|
||||
[], [61], [],
|
||||
[], [18], [],
|
||||
[], [1963#place(center + horizon, ellipse(width: 2.5em, height: 2em, stroke: std.red))], [],
|
||||
[], [35], [],
|
||||
[], [42], [],
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#focus-slide(theme: "mandarine")[What's Out There?]
|
||||
= What's out there?
|
||||
|
||||
== Existing Type Checkers
|
||||
|
||||
#v(1fr)
|
||||
#grid(
|
||||
columns: (1fr, 1fr),
|
||||
column-gutter: 3em,
|
||||
align: center + horizon,
|
||||
image("../figs/mypy_logo.svg"),
|
||||
image("../figs/pyright-logo.png"),
|
||||
)
|
||||
#v(1fr)
|
||||
|
||||
== Community Packages for Pandas
|
||||
|
||||
=== Strictly Typing Pandas
|
||||
#{
|
||||
set text(size: 0.8em)
|
||||
emph(link("https://strictly-typed-pandas.readthedocs.io"))
|
||||
}
|
||||
|
||||
- Static typing of schema
|
||||
- Quite limited
|
||||
|
||||
=== Pandera
|
||||
#{
|
||||
set text(size: 0.8em)
|
||||
emph(link("https://pandera.readthedocs.io"))
|
||||
}
|
||||
|
||||
- Dynamic verification and data validation
|
||||
- Powerful but verbose
|
||||
@@ -0,0 +1,572 @@
|
||||
#import "../requirements.typ": *
|
||||
#import "@preview/curryst:0.6.0": prooftree, rule
|
||||
#import "@preview/pinit:0.2.2": pin, pinit-point-from
|
||||
#import codly: codly, codly-enable, codly-disable, local
|
||||
#import "@preview/cetz:0.5.2" as cetz: canvas, draw, tree
|
||||
#import "@preview/cetz-plot:0.1.4": smartart
|
||||
#import "@preview/conch:0.1.0"
|
||||
|
||||
#focus-slide(theme: "forest")[Making Python Better]
|
||||
= Making Python Better
|
||||
|
||||
== Strict Typing Rules
|
||||
|
||||
#let syntax(body) = {
|
||||
set text(weight: "bold", fill: rgb(100, 50, 0))
|
||||
body
|
||||
}
|
||||
|
||||
#slide(repeat: 5, self => {
|
||||
let (uncover, only, alternatives-match) = utils.methods(self)
|
||||
let typing-rule = prooftree(
|
||||
rule(
|
||||
name: smallcaps[(T-Tern)],
|
||||
uncover("3-", $Gamma tack "t"_1: "bool"$),
|
||||
uncover("4-", $Gamma tack "t"_21: "T"$),
|
||||
uncover("4-", $Gamma tack "t"_22: "T"$),
|
||||
alternatives-match((
|
||||
"1": [],
|
||||
"2-4": $"t"_21 #syntax[if] "t"_1 #syntax[else] "t"_22$,
|
||||
"5-": $Gamma tack "t"_21 #syntax[if] "t"_1 #syntax[else] "t"_22: "T"$,
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
show raw: it => {
|
||||
show regex("pin\d+"): it => pin(int(it.text.slice(3)))
|
||||
it
|
||||
}
|
||||
|
||||
figure(
|
||||
```python
|
||||
value = pin1e1pin2 if pin3conditionpin4 else pin5e2pin6
|
||||
```
|
||||
)
|
||||
|
||||
pause
|
||||
|
||||
/*
|
||||
pinit-point-from(
|
||||
(1, 2),
|
||||
offset-dx: -20pt,
|
||||
body-dx: -0.8em,
|
||||
body-dy: -0.2em,
|
||||
$"t"_21$
|
||||
)
|
||||
|
||||
pinit-point-from(
|
||||
(5, 6),
|
||||
offset-dx: 20pt,
|
||||
body-dx: 0.2em,
|
||||
body-dy: -0.2em,
|
||||
$"t"_22$
|
||||
)
|
||||
|
||||
pinit-point-from(
|
||||
(3, 4),
|
||||
offset-dx: 0pt,
|
||||
pin-dx: 0pt,
|
||||
body-dx: -0.2em,
|
||||
body-dy: 0.2em,
|
||||
$"t"_1$
|
||||
)
|
||||
*/
|
||||
|
||||
v(2cm)
|
||||
|
||||
figure(typing-rule)
|
||||
})
|
||||
|
||||
#codly(
|
||||
highlights: (
|
||||
(
|
||||
line: 2,
|
||||
start: 9,
|
||||
tag: [#set text(size: 1.25em);```python int```],
|
||||
fill: std.yellow
|
||||
),
|
||||
)
|
||||
)
|
||||
#figure(
|
||||
```python
|
||||
value: float
|
||||
value = 2026
|
||||
```
|
||||
)
|
||||
|
||||
#pause
|
||||
|
||||
Liskov Substitution Principle $->$ Subsumption Rule
|
||||
|
||||
#figure(
|
||||
prooftree(
|
||||
rule(
|
||||
name: smallcaps[(T-Sub)],
|
||||
$Gamma tack "t": "S"$,
|
||||
$"S" <: "T"$,
|
||||
$Gamma tack "t": "T"$,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
== Enforcing Rules
|
||||
|
||||
/*
|
||||
#set enum(
|
||||
full: true,
|
||||
numbering: (..n) => strong(
|
||||
numbering(
|
||||
if n.pos().len() == 1 {
|
||||
"1."
|
||||
} else {
|
||||
"a."
|
||||
},
|
||||
n.pos().last()
|
||||
)
|
||||
)
|
||||
)
|
||||
Steps:
|
||||
+ Parsing
|
||||
+ Type-checking
|
||||
+ + Reporting
|
||||
+ Code generation
|
||||
*/
|
||||
|
||||
#let palette1 = (
|
||||
std.purple,
|
||||
std.blue,
|
||||
std.orange,
|
||||
std.green,
|
||||
)
|
||||
#let palette2 = (
|
||||
std.red,
|
||||
std.orange,
|
||||
std.green,
|
||||
std.blue,
|
||||
)
|
||||
#let steps = (
|
||||
[Parsing],
|
||||
[Type-checking],
|
||||
[Reporting],
|
||||
[Code generation],
|
||||
)
|
||||
#let palette = palette2.map(c => c.lighten(50%))
|
||||
|
||||
#{
|
||||
slide(repeat: 4, self => context {
|
||||
let max-step-height = calc.max(..steps.map(s => measure(box(width: 5em, s)).height))
|
||||
|
||||
align(left + horizon, canvas({
|
||||
smartart.process.chevron(
|
||||
steps.map(s => box(height: max-step-height, align(center + horizon, s))).slice(0, calc.min(steps.len(), self.subslide)),
|
||||
step-style: palette,
|
||||
steps: (
|
||||
max-width: 5em,
|
||||
),
|
||||
)
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
#let header-process(n) = place(
|
||||
top + right,
|
||||
{
|
||||
set text(size: 0.5em)
|
||||
canvas({
|
||||
smartart.process.chevron(
|
||||
steps,
|
||||
step-style: palette.enumerate().map(((i, c)) => {
|
||||
if i == n {c}
|
||||
else {c.lighten(70%)}
|
||||
}),
|
||||
steps: (max-width: 5em),
|
||||
equal-length: true
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
#header-process(0)
|
||||
#{
|
||||
show raw: set text(size: 0.7em)
|
||||
grid(
|
||||
columns: (11em, auto),
|
||||
column-gutter: 1em,
|
||||
row-gutter: 0.2em,
|
||||
align: (x, y) => if y == 0 {center} else if x == 0 and y == 2 {top + right} else {left},
|
||||
grid.header[*Source*][*Raw AST*],
|
||||
```python
|
||||
value = 3.0 + int("4")
|
||||
```,
|
||||
grid.cell(rowspan: 2, uncover("2-", ```python
|
||||
Module(
|
||||
body=[
|
||||
Assign(
|
||||
targets=[
|
||||
Name(id='value')],
|
||||
value=BinOp(
|
||||
left=Constant(value=3.0),
|
||||
op=Add(),
|
||||
right=Call(
|
||||
func=Name(id='int'),
|
||||
args=[
|
||||
Constant(value='4')],
|
||||
keywords=[])))])
|
||||
```)),
|
||||
uncover("2-", canvas({
|
||||
draw.bezier-through(
|
||||
(0, 0),
|
||||
(1.5, -2.5),
|
||||
(5, -4),
|
||||
mark: (end: ">", fill: black),
|
||||
name: "arrow"
|
||||
)
|
||||
draw.content(
|
||||
(1.5, -2.5),
|
||||
```python ast.parse(source)```,
|
||||
anchor: "north-east",
|
||||
padding: 3pt,
|
||||
)
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
|
||||
#header-process(0)
|
||||
#{
|
||||
show raw: set text(size: 0.7em)
|
||||
grid(
|
||||
columns: (auto, auto),
|
||||
column-gutter: 1em,
|
||||
row-gutter: 0.2em,
|
||||
align: (x, y) => if y == 0 {center} else if x == 0 and y == 2 {top + right} else {left},
|
||||
grid.header[*Raw AST*][*Custom AST*],
|
||||
```python
|
||||
Module(
|
||||
body=[
|
||||
Assign(
|
||||
targets=[
|
||||
Name(id='value')],
|
||||
value=BinOp(
|
||||
left=Constant(value=3.0),
|
||||
op=Add(),
|
||||
right=Call(
|
||||
func=Name(id='int'),
|
||||
args=[
|
||||
Constant(value='4')],
|
||||
keywords=[])))])
|
||||
```,
|
||||
local(
|
||||
inset: (x: 0.32em, y: 0.15em),
|
||||
lang-outset: (x: 0.32em, y: 0.2em),
|
||||
```AST
|
||||
AssignStmt
|
||||
├── targets
|
||||
│ └── [0] VariableExpr
|
||||
│ └── name: value
|
||||
└── value
|
||||
└── BinaryExpr
|
||||
├── left
|
||||
│ └── LiteralExpr
|
||||
│ └── value: 3.0
|
||||
├── operator: Add
|
||||
└── right
|
||||
└── CallExpr
|
||||
├── callee
|
||||
│ └── VariableExpr
|
||||
│ └── name: int
|
||||
├── arguments
|
||||
│ └── [0] LiteralExpr
|
||||
│ └── value: '4'
|
||||
└── keywords
|
||||
```
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
#header-process(1)
|
||||
|
||||
#let cetz-canvas = touying-reduce.with(cetz)
|
||||
#let typing-tree = cetz-canvas({
|
||||
draw.set-style(content: (padding: 0.5em))
|
||||
tree.tree(
|
||||
name: "tree",
|
||||
([`type_of`(```py 3.0 + int("4")```)],
|
||||
[`type_of`(```py 3.0```)],
|
||||
([`type_of`(```py int("4")```)],
|
||||
[`type_of`(```py int```)],
|
||||
[`type_of`(```py "4"```)],
|
||||
)
|
||||
)
|
||||
)
|
||||
let type(pos, typ) = {
|
||||
draw.content(
|
||||
"tree.g" + pos.map(str).join("-") + ".south",
|
||||
box(
|
||||
inset: (x: 0.4em, y: 0.2em),
|
||||
stroke: std.orange,
|
||||
fill: std.orange.lighten(90%),
|
||||
radius: 0.4em,
|
||||
{
|
||||
set text(size: 0.8em)
|
||||
raw(lang: "python", typ)
|
||||
}
|
||||
),
|
||||
padding: 0pt,
|
||||
anchor: "north"
|
||||
)
|
||||
}
|
||||
|
||||
(pause,)
|
||||
type((0, 0), "float")
|
||||
type((0, 1, 1), "str")
|
||||
(pause,)
|
||||
type((0, 1, 0), "(Any, /) -> int")
|
||||
(pause,)
|
||||
type((0, 1), "int")
|
||||
(pause,)
|
||||
draw.content(
|
||||
(
|
||||
rel: (3, 0),
|
||||
to: (
|
||||
"tree.g0.south",
|
||||
50%,
|
||||
"tree.g0-1.north",
|
||||
)
|
||||
),
|
||||
{
|
||||
set text(size: 0.8em)
|
||||
```py float.__add__(int) -> float```
|
||||
},
|
||||
anchor: "west"
|
||||
)
|
||||
(pause,)
|
||||
type((0,), "float")
|
||||
})
|
||||
|
||||
#align(center + horizon, typing-tree)
|
||||
|
||||
---
|
||||
|
||||
#let midas-outputs = (
|
||||
check: read("../figs/midas_output.txt"),
|
||||
types: read("../figs/midas_output2.txt"),
|
||||
)
|
||||
|
||||
#let midas-cmd(args, stdin, files) = {
|
||||
(
|
||||
stdout: midas-outputs.at(args.first()),
|
||||
exit-code: 0
|
||||
)
|
||||
}
|
||||
|
||||
#let frames = conch.terminal-frames(
|
||||
system: conch.system(
|
||||
plugins: (
|
||||
("midas", midas-cmd),
|
||||
),
|
||||
files: (
|
||||
"script.py": read("../figs/script.py")
|
||||
),
|
||||
hostname: "XANA",
|
||||
),
|
||||
user: "louis",
|
||||
commands: (
|
||||
"cat script.py",
|
||||
"midas check script.py",
|
||||
"midas types script.py",
|
||||
"",
|
||||
),
|
||||
mode: "key-frames",
|
||||
show-cursor: false,
|
||||
theme: "monokai",
|
||||
height: 300pt
|
||||
)
|
||||
|
||||
#slide(repeat: frames.len() - 2, self => [
|
||||
#header-process(2)
|
||||
#figure(
|
||||
align(left + top, frames.at(self.subslide))
|
||||
)
|
||||
])
|
||||
|
||||
#header-process(3)
|
||||
|
||||
#align(
|
||||
center + horizon,
|
||||
grid(
|
||||
columns: 3,
|
||||
column-gutter: 2em,
|
||||
row-gutter: 1em,
|
||||
[AST], none, none,
|
||||
text(size: 2em)[+],
|
||||
uncover("2-", text(size: 2em)[$stretch(->)^#{set text(size: 0.6em);```py ast.unparse(tree)```}$]),
|
||||
uncover("2-")[Code],
|
||||
[Typing\ Judgements],
|
||||
)
|
||||
)
|
||||
|
||||
// What's the point? What about constraints?
|
||||
|
||||
== Runtime Verification
|
||||
|
||||
#{
|
||||
show raw: set text(size: 0.7em)
|
||||
|
||||
grid(
|
||||
columns: 2,
|
||||
column-gutter: 1em,
|
||||
row-gutter: 1em,
|
||||
align: horizon,
|
||||
rotate(-90deg, reflow: true)[*Source*],
|
||||
```python
|
||||
from lib import fetch_data
|
||||
unknown = fetch_data()
|
||||
speed = cast(Meter, unknown)
|
||||
```,
|
||||
uncover("2-", rotate(-90deg, reflow: true)[*Generated*]),
|
||||
uncover("2-", ```python
|
||||
from lib import fetch_data
|
||||
unknown = fetch_data()
|
||||
__midas_a0__ = unknown
|
||||
assert isinstance(__midas_a0__, float), f'script.py:L3:9: CastError: Cannot cast {type(__midas_a0__).__name__} to float'
|
||||
speed = __midas_a0__
|
||||
del __midas_a0__
|
||||
```)
|
||||
)
|
||||
}
|
||||
|
||||
== Customization
|
||||
|
||||
#{
|
||||
show raw: set text(size: 0.8em)
|
||||
item-by-item()[
|
||||
- Domain Specific Types (e.g. units, scores, transformed data)
|
||||
```midas
|
||||
type Meter = float
|
||||
extend Meter {
|
||||
def __add__: fn(Meter, /) -> Meter
|
||||
def __truediv__: fn(float, /) -> Meter
|
||||
}
|
||||
```
|
||||
- Dependent Types (i.e. with value constraints)
|
||||
```midas
|
||||
predicate in_range(mn: float, mx: float)(v: float) = mn <= v & v <= mx
|
||||
type Age = int where in_range(0.0, 150.0)(_)
|
||||
```
|
||||
]
|
||||
}
|
||||
|
||||
== Advanced Typing
|
||||
|
||||
Defining a dataframe schema
|
||||
```midas
|
||||
alias Data = Frame[
|
||||
name: str,
|
||||
height: Meter,
|
||||
age: Age
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Using a dataframe schema
|
||||
```python
|
||||
import pandas as pd
|
||||
df = cast(Data, pd.read_csv("data.csv"))
|
||||
```
|
||||
|
||||
#pause
|
||||
|
||||
Generates runtime checks
|
||||
|
||||
|
||||
#let python-outputs = (
|
||||
data1: read("../figs/python_output.txt"),
|
||||
data2: read("../figs/python_output2.txt"),
|
||||
)
|
||||
|
||||
#let python-cmd(args, stdin, files, output: "") = {
|
||||
(
|
||||
stdout: output,
|
||||
exit-code: 0,
|
||||
)
|
||||
}
|
||||
|
||||
#let make-cast-error(output) = conch.terminal-block(
|
||||
system: conch.system(
|
||||
plugins: (
|
||||
("midas", (..args) => (stdout: "", exit-code: 0)),
|
||||
("python", python-cmd.with(output: output)),
|
||||
),
|
||||
hostname: "XANA",
|
||||
),
|
||||
user: "louis",
|
||||
show-cursor: false,
|
||||
theme: "monokai",
|
||||
height: 150pt,
|
||||
width: 660pt,
|
||||
```
|
||||
midas compile script.py -t types.midas
|
||||
python build/midas/script.py
|
||||
```
|
||||
)
|
||||
|
||||
#figure(
|
||||
align(
|
||||
left + top,
|
||||
make-cast-error(python-outputs.data2),
|
||||
)
|
||||
)
|
||||
|
||||
---
|
||||
|
||||
```python
|
||||
mean_height = df["height"].mean()
|
||||
```
|
||||
|
||||
#pause
|
||||
|
||||
#figure(
|
||||
align(
|
||||
left + top,
|
||||
conch.terminal-block(
|
||||
system: conch.system(
|
||||
plugins: (
|
||||
(
|
||||
"midas",
|
||||
(..args) => (
|
||||
stdout: read("../figs/midas_df_types.txt"),
|
||||
exit-code: 0
|
||||
)
|
||||
),
|
||||
),
|
||||
hostname: "XANA",
|
||||
),
|
||||
user: "louis",
|
||||
show-cursor: false,
|
||||
theme: "monokai",
|
||||
```
|
||||
midas types script.py -t types.midas
|
||||
```
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
---
|
||||
|
||||
$
|
||||
overline(x) = 1 / n sum_(i=0)^n x_i
|
||||
$
|
||||
|
||||
#pause
|
||||
|
||||
$
|
||||
"type_of"(overline(x)) &= "type_of"( ("type_of"(x) + "type_of"(x)) / "type_of"(n) )\
|
||||
pause
|
||||
&= "type_of"( ("Meter" + "Meter") / "int") pause = "Meter"
|
||||
$
|
||||
@@ -0,0 +1,67 @@
|
||||
#import "../requirements.typ": *
|
||||
#import codly: codly
|
||||
|
||||
#focus-slide(theme: "neon")[Current Status and Future]
|
||||
= Current Status and Future
|
||||
|
||||
== What Midas offers
|
||||
|
||||
#v(-0.5em)
|
||||
#item-by-item[
|
||||
|
||||
- Working *static type checking* of a good subset of Python (including dataframes)
|
||||
|
||||
- *Custom type* definition language for domain-specific usages (supports generics, method overloads, dependent types, frame schemas, named predicates and more)
|
||||
|
||||
- Toolbox *CLI* with useful debug features
|
||||
|
||||
- Generation of *runtime assertions*
|
||||
|
||||
- Stubs for *seamless integration* with other type checkers
|
||||
]
|
||||
|
||||
== What needs work
|
||||
|
||||
#{
|
||||
show raw: set text(size: 0.85em)
|
||||
item-by-item[
|
||||
- Reference types (side effects)
|
||||
```python
|
||||
def modify_column(df: pd.DataFrame):
|
||||
df["col"] = df["col"].astype(float)
|
||||
```
|
||||
- Reverse operators
|
||||
```python
|
||||
a = 1.0 + 1 # float.__add__(int)
|
||||
b = 1 + 1.0 # float.__radd__(int)
|
||||
```
|
||||
- Logical short-circuit bypass
|
||||
#codly(
|
||||
highlights: (
|
||||
(
|
||||
line: 1,
|
||||
start: 15,
|
||||
tag: box(inset: (x: 0.5em))[_Always evaluated_ ],
|
||||
fill: std.red.lighten(20%)
|
||||
),
|
||||
)
|
||||
)
|
||||
```python
|
||||
value = v1 or cast(Type, v2)
|
||||
```
|
||||
]
|
||||
}
|
||||
|
||||
== Possible extensions
|
||||
|
||||
#item-by-item[
|
||||
|
||||
- Visitor pattern $->$ easy to extend supported Python syntax
|
||||
|
||||
- Multi-file projects (```python import``` statements)
|
||||
|
||||
- Constraint solver
|
||||
$
|
||||
v in [1; 6] => v > 0
|
||||
$
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#focus-slide(theme: "smoke")[Thanks for listening !]
|
||||
= Thanks for listening !
|
||||
|
||||
== Repositories
|
||||
|
||||
#let repos = (
|
||||
([Code], path("../../poster/figs/qr_gitea.svg"), "https://git.kb28.ch/HEL/midas"),
|
||||
([Code (mirror)], path("../../poster/figs/qr_github.svg"), "https://github.com/LordBaryhobal/midas"),
|
||||
([Docs], path("../../poster/figs/qr_docs.svg"), "https://git.kb28.ch/HEL/TB-Docs"),
|
||||
)
|
||||
|
||||
#let repo(name, code, url) = {
|
||||
stack(
|
||||
dir: ttb,
|
||||
spacing: 0.2em,
|
||||
strong(text(size: 0.8em, name)),
|
||||
image(code, width: 3cm),
|
||||
{
|
||||
set text(size: 0.6em)
|
||||
link(url)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#v(1fr)
|
||||
|
||||
#grid(
|
||||
columns: (1fr, 1fr),
|
||||
align: center + horizon,
|
||||
row-gutter: 2em,
|
||||
repo(..repos.at(0)),
|
||||
repo(..repos.at(1)),
|
||||
grid.cell(colspan: 2, repo(..repos.at(2)))
|
||||
)
|
||||
|
||||
#v(1fr)
|
||||
@@ -0,0 +1,3 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#show: appendix
|
||||
@@ -11,6 +11,3 @@
|
||||
)
|
||||
|
||||
#init-acronyms(acronyms)
|
||||
|
||||
// Don't think this needs a long form on first use
|
||||
#mark-acr-used("CSV")
|
||||
@@ -17,7 +17,7 @@ $ p = (i, n, r) $
|
||||
A *Function* has a param spec $S$ and a return type $R$:
|
||||
$ F = (S, R) $
|
||||
|
||||
= Main rules
|
||||
= Main Rules
|
||||
|
||||
We want to define a rule for checking structural subtyping of functions, i.e. to check when $F_1 <: F_2$
|
||||
|
||||
@@ -39,7 +39,7 @@ After mapping parameters of $S_1$ and $S_2$, types must be checked such that if
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
= Detailed rules for *ParamSpec* subtyping
|
||||
= Detailed Rules for *ParamSpec* Subtyping
|
||||
|
||||
#gc.info(title: [Notation])[
|
||||
In the following equations:
|
||||
@@ -51,7 +51,7 @@ After mapping parameters of $S_1$ and $S_2$, types must be checked such that if
|
||||
- $req(S, alpha)$ is a predicate checking whether $alpha$ is required in $S$
|
||||
]
|
||||
|
||||
== Arguments of $S_2$ are compatible with $S_1$ <cond-1>
|
||||
== Arguments of $S_2$ are Compatible with $S_1$ <cond-1>
|
||||
|
||||
Formally, the condition is:
|
||||
$
|
||||
@@ -77,7 +77,7 @@ $
|
||||
)
|
||||
$
|
||||
|
||||
== No additional required arguments in $S_1$ <cond-2>
|
||||
== No Additional Required Arguments in $S_1$ <cond-2>
|
||||
|
||||
Formally, the condition is:
|
||||
$
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
|
||||
#figure(
|
||||
code(midas-typer-members-code),
|
||||
caption: [Midas Typer Members]
|
||||
caption: [Midas typer members]
|
||||
) <fig:midas-typer-members>
|
||||
@@ -16,7 +16,7 @@
|
||||
lang: "python",
|
||||
read("../code/variance_inferrer.py")
|
||||
)),
|
||||
caption: [Implementation of variance `VarianceInferrer`]
|
||||
caption: [Implementation of `VarianceInferrer`]
|
||||
) <fig:midas-variance-inferrer>
|
||||
|
||||
#figure(
|
||||
@@ -25,5 +25,5 @@
|
||||
lang: "python",
|
||||
read("../code/variance_manager.py")
|
||||
)),
|
||||
caption: [Implementation of variance `VarianceManager`]
|
||||
caption: [Implementation of `VarianceManager`]
|
||||
) <fig:midas-variance-manager>
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#import "requirements.typ": isc-hei-bthesis
|
||||
#import isc-hei-bthesis: *
|
||||
#import "@preview/codly:1.3.0": codly-init, codly
|
||||
#import "@local/codly:1.3.1": codly-init, codly // https://github.com/PhotonQuantum/codly/tree/fix/highlight-baseline
|
||||
#import "@preview/codly-languages:0.1.10": codly-languages
|
||||
#import "../meta.typ"
|
||||
#import "utils.typ": include-offset
|
||||
@@ -86,6 +86,10 @@
|
||||
outlined: false,
|
||||
)
|
||||
|
||||
// Mark all acronyms as used to have short forms in outlines
|
||||
#context for a in state("acrostiche-acronyms").final() {
|
||||
acused(a.first())
|
||||
}
|
||||
|
||||
// Table of figures/tables/listings
|
||||
#context {
|
||||
@@ -95,7 +99,10 @@
|
||||
show pagebreak: none
|
||||
tab(
|
||||
title: page-title(i18n(f, "figure-table-title"), mult: 1, top: 0.4em, bottom: 0.4em),
|
||||
target: figure.where(kind: image),
|
||||
target: selector.or(
|
||||
figure.where(kind: image),
|
||||
figure.where(kind: "ruleset"),
|
||||
)
|
||||
)
|
||||
tab(
|
||||
title: page-title(i18n(f, "table-table-title"), mult: 1, top: 0.4em, bottom: 0.4em),
|
||||
@@ -109,6 +116,10 @@
|
||||
)
|
||||
}
|
||||
|
||||
#acresetall()
|
||||
// Don't think this needs a long form on first use
|
||||
#mark-acr-used("CSV")
|
||||
|
||||
////////////////////////////
|
||||
// Let's get started folks!
|
||||
////////////////////////////
|
||||
@@ -148,6 +159,10 @@ This structure may vary depending on the field of study, but these elements are
|
||||
You can also change the order or the names of the sections, for instance, if you want to put the state of the art before the introduction, or if you want to add a section on methodology before the results.
|
||||
*/
|
||||
|
||||
#show heading.where(level: 1): set heading(supplement: context {
|
||||
let f = inc.global-language.get()
|
||||
i18n(f, "chapter-title")
|
||||
})
|
||||
#include "chapters/01_introduction.typ"
|
||||
#include "chapters/02_state_of_the_art.typ"
|
||||
#include "chapters/03_theory.typ"
|
||||
@@ -191,11 +206,14 @@ You can also change the order or the names of the sections, for instance, if you
|
||||
|
||||
#counter(heading).update(0)
|
||||
|
||||
#set heading(numbering: "A.1", supplement: [Appendix])
|
||||
#set heading(numbering: "A.1")
|
||||
|
||||
#set figure(numbering: (..nums) => counter(heading).display("A.1") + "." + numbering("1", ..nums))
|
||||
#set heading(outlined: false)
|
||||
#show heading.where(level: 1): set heading(outlined: true)
|
||||
#show heading.where(level: 1): set heading(
|
||||
outlined: true,
|
||||
supplement: [Appendix]
|
||||
)
|
||||
#counter(figure.where(kind: image)).update(0)
|
||||
#counter(figure.where(kind: table)).update(0)
|
||||
#counter(figure.where(kind: raw)).update(0)
|
||||
@@ -227,4 +245,18 @@ You can also change the order or the names of the sections, for instance, if you
|
||||
relationship: "supplement",
|
||||
mime-type: "application/pdf",
|
||||
description: "Midas User Manual"
|
||||
)
|
||||
)
|
||||
|
||||
#context {
|
||||
let targets = query(ref).map(x => x.target)
|
||||
let figures = query(figure)
|
||||
for fig in figures {
|
||||
if fig.at("caption", default: none) != none and fig.at("label", default: none) == none {
|
||||
panic(repr(fig) + " is missing a label")
|
||||
}
|
||||
}
|
||||
let unreferenced = figures.map(x => x.at("label", default: none)).filter(x => x != none and not str(x).ends-with(regex(`:\d+`.text)) and x not in targets)
|
||||
if unreferenced.len() > 0 {
|
||||
panic("Unreferenced labels: " + unreferenced.map(str).join(", "))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#import "../requirements.typ": isc-hei-bthesis
|
||||
#import isc-hei-bthesis: todo
|
||||
#import "../utils.typ": quote-block
|
||||
#import "@preview/gentle-clues:1.3.1" as gc
|
||||
|
||||
= Introduction <chap:intro>
|
||||
|
||||
@@ -8,9 +9,7 @@
|
||||
Any one who considers arithmetical methods of producing random digits is, of course, in a state of sin
|
||||
]
|
||||
|
||||
Dogs are often said to be man's best friend for their unwavering loyalty. As human beings, we thrive on patterns and predictability. On the other hand, our little animal friends can sometimes be irrational and surprise us with their unpredictable behavior. Computers however, these thought-less automatic machines made of metal, would be much better companions.
|
||||
|
||||
By their fundamental essence, computers and other similar machines are deterministic. As developers, we simply give them series of instructions, algorithms, functions and other scripts. Their one and only job is to execute our commands, one after the other, sometimes at the same time. An addition is always an addition, a byte is always a byte, and most importantly, a dog is always a dog.
|
||||
By their fundamental essence, computers and other similar machines are deterministic. As developers, we simply give them series of instructions, algorithms. Their one and only job is to execute our commands, one after the other, sometimes at the same time. An addition is always an addition, a byte is always a byte.
|
||||
|
||||
When programming, developers use formal languages to instruct their devices to do particular tasks. Theses languages are designed to be unambiguous and generally explicit about what they describe. To the machine interpreting that written text, only one meaning is possible. "But", you may say, "sometimes there are bugs when I run my program". Of course, bugs are an inherent part of programming, but not because the machine might not understand you, because you explained it wrongly.
|
||||
Errors and bugs are always caused by the human factor of computer science.#footnote[Notable exceptions are of course hardware failures which are a totally separate kind of issue]
|
||||
@@ -25,11 +24,11 @@ Additionally, the Python language uses dynamic typing, a design principle allowi
|
||||
|
||||
== Motivation
|
||||
|
||||
As explained above, Python is a very flexible language, sometimes too flexible. For beginners trying to learn programming, this can be a double-edged sword. One the one hand, the language is quite lenient and allows mixing data types, reusing variables with different types and passing values that are not at all what was expected by a function but still have the correct attributes or methods. On the other hand, it can lead to confusing behavior, when a parameter does not fit the developer's expectation or a variable is changed in some part of the code to a completely different kind of value. At the professional level, this can also prove dangerous. Mixing medical measurements or having a production service crash because of a type error can become an expensive mistake with real-world impact.
|
||||
As explained above, Python is a very flexible language, sometimes too flexible. For beginners trying to learn programming, this can be a double-edged sword. On the one hand, the language is quite lenient and allows mixing data types, reusing variables with different types and passing values that are not at all what was expected by a function but still have the correct attributes or methods. On the other hand, it can lead to confusing behavior, when a parameter does not fit the developer's expectations or a variable is changed in some part of the code to a completely different kind of value. At the professional level, this can also prove dangerous. Mixing medical measurements or having a production service crash because of a type error can become an expensive mistake with real-world impact.
|
||||
|
||||
Secondly, as every Java developer can tell you, runtime errors are the worst. The dreaded `NullPointerException` continues to haunt many developers. Although some runtime errors like this are quite loud and can crash an hour-long computation process, others are much more subtle and tricky to catch. A famous example of this is the historical crash of NASA's Mars Climate Orbiter in 1999, which failed its orbital insertion due to a unit error mixing metric and imperial measurements.@MCOReport
|
||||
|
||||
Lastly, designing a safe code base and including tests to check that values conform to what is expected can be a tedious and often unrewarding process. Simplifying this process is one of the motivation for the famous Python library Pydantic, allowing users to define data structures that can validate inputs when instantiated.
|
||||
Lastly, designing a safe code base and including tests to check that values conform to what is expected can be a tedious and often unrewarding process. Simplifying this process is one of the motivations for the famous Python library Pydantic, allowing users to define data structures that can validate inputs when instantiated.
|
||||
|
||||
== Objectives
|
||||
|
||||
@@ -42,3 +41,7 @@ The system should not impede on Python's dynamic typing philosophy. It should en
|
||||
It must be flexible to allow checking various kinds of constraints (e.g. value domain, geometric shape, distribution, etc.) and allow extension to add support for more Python features and packages.
|
||||
|
||||
Finally, the system must be simple to use for average Python developers, and be able to seamlessly integrate into existing Python code.
|
||||
|
||||
#gc.info[
|
||||
This report was written using Typst. Its source files can be found at https://git.kb28.ch/HEL/TB-Docs.
|
||||
]
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
First of all, before reinventing the wheel and creating a completely redundant tool, we must look at what already exists in Python's rich environment and beyond.
|
||||
|
||||
The Python Language includes a dedicated syntax for what are called "type hints", as defined in PEP 484 since Python 3.5@PEP484.
|
||||
Type hints are annotations that users can add to variables and functions to specify information about the type of some values.
|
||||
The Python language includes a dedicated syntax for what are called "type hints", as defined in PEP 484 since Python 3.5@PEP484.
|
||||
Type hints are annotations that users can add to variables and functions to specify information about the types of some values.
|
||||
|
||||
Python also natively provides a `typing` module with several tools developers can use to better describe their code. This includes but is not limited to `Generic` and `TypeVar` for generic types, the `cast()` function, the `TypeAlias` class to explicitly mark some assignments as type aliases, and `Protocol`, a multipurpose machinery allowing the definition of interface-like classes (defined since Python 3.8 in PEP 544@PEP544). The latter can notably be used to remove some ambiguity around duck-typing and make it more explicit, by indicating that some value implements some method or has some specific attribute.\
|
||||
However, this system is difficult to scale to dataframe and more complex type operations. Additionally, Python remains a fundamentally dynamically typed language which only really cares about the values' base types and whether they possess specific attributes or methods.
|
||||
However, this system is difficult to scale to dataframes and more complex type operations. Additionally, Python remains a fundamentally dynamically typed language which only really cares about the values' base types and whether they possess specific attributes or methods.
|
||||
|
||||
Of course, type checkers have long been integrated into IDEs, like MyPy and Pyright. These often provide comprehensive type checking, supporting all aforementioned features of the `typing` module and more, and often offer multiple levels of strictness. They do lack some features that could give users more freedom in defining type aliases such as a generic type that is a subtype of its own parameter (e.g. a `Price[EUR]` is an amount of `EUR`, which is itself a `float`). They do not support constraint type either and only provide static analysis, without the ability to generate runtime assertions. Type checking complex structures like `numpy` arrays or `pandas` dataframes is also complicated, if not impossible.
|
||||
Of course, type checkers have long been integrated into IDEs, like MyPy and Pyright. These often provide comprehensive type checking, supporting all aforementioned features of the `typing` module and more, and often offer multiple levels of strictness. They do lack some features that could give users more freedom in defining type aliases such as a generic type that is a subtype of its own parameter (e.g. a `Price[EUR]` is an amount of `EUR`, which is itself a `float`). They do not support constraint types either and only provide static analysis, without the ability to generate runtime assertions. Type checking complex structures like `numpy` arrays or `pandas` dataframes is also complicated, if not impossible.
|
||||
|
||||
In Python's ecosystem, there are two interesting projects which do however try to help with typing dataframes. The first, focused on statically typing schemas, is called #fn-link(<strictly-typed-pandas>, "https://strictly-typed-pandas.readthedocs.io")[_Strictly Typed Pandas_]. It allows declaring schemas as simple class definitions and even provides runtime verification using a simple `@typechecked` decorator. In spite of that, it does seem to lack proper type checking of operations on dataframes and columns.
|
||||
The other project worth mentioning is called #fn-link(<pandera>, "https://pandera.readthedocs.io")[_Pandera_]@niels_bantilan-proc-scipy-2020. This Python package provides a wide range of features to perform runtime verification of dataframe schemas and data validation, such as statistical analysis. While it does seem to be a rather useful and complete tool, it does not cover our static type checking goal and has quite a verbose syntax.
|
||||
|
||||
@@ -65,7 +65,7 @@ The notations for context are as follows:
|
||||
[the context extended with the binding of $"t"$ to the type $"T"$],
|
||||
))
|
||||
|
||||
Finally, the rules expressed in @sec:theory-syntax are written in the form of theorems using the following mathematical syntaxes:
|
||||
Finally, the rules expressed in @sec:theory-typing are written in the form of theorems using the following mathematical syntaxes:
|
||||
|
||||
#align(center, grid(
|
||||
columns: 2,
|
||||
@@ -81,34 +81,34 @@ Finally, the rules expressed in @sec:theory-syntax are written in the form of th
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
== Syntax rules <sec:theory-syntax>
|
||||
== Syntax Rules <sec:theory-syntax>
|
||||
|
||||
In this section, we define syntax rules for both Python and our type definition language Midas. Obviously, Python already has syntax rules defining what is or is not valid code. What is defined in @sec:theory-syntax-python are a subset of constructs which our type checker will be able to process.
|
||||
|
||||
=== Python syntax <sec:theory-syntax-python>
|
||||
=== Python Syntax <sec:theory-syntax-python>
|
||||
|
||||
@tab:python-syntax-rules lists mosts Python constructs we want to support with our type checker (notable exceptions are listed below). This subset of the Python language is sufficient to express a wide range of programs, including data processing pipelines and moderately complex scripts.
|
||||
|
||||
#figure(
|
||||
python-syntax,
|
||||
caption: [Python syntax rules],
|
||||
supplement: [Table],
|
||||
kind: table,
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:python-syntax-rules>
|
||||
|
||||
Some syntaxes are simplified or omitted from @tab:python-syntax-rules for the sake of conciseness, readability and scope. Indeed, this work's primary focus is not on the theoretical and formal approach to type checking but rather on the concrete application of it to produce a useful tool for developers. For example, function signature in Python are rather complex and flexible, allowing positional-only, keyword-only and mixed parameters, argument sinks, and default values. Expressing this level of flexibility in a formal definition as those presented above is tricky and not necessarily pertinent to this work.
|
||||
|
||||
Other syntaxes not referenced in this report are voluntarily left out because they either fall out of the scope of this work or are either not fundamental to the language or not necessary to most developers. Such constructs include classes, match statements, while loops, `for`/`else` blocks and the walrus operator~(`:=`).
|
||||
Other syntaxes not referenced in this report are voluntarily left out because they either fall out of the scope of this work, are not fundamental to the language or not necessary to most developers. Such constructs include classes, match statements, while loops, `for`/`else` blocks and the walrus operator~(`:=`).
|
||||
|
||||
=== Midas syntax <sec:theory-syntax-midas>
|
||||
=== Midas Syntax <sec:theory-syntax-midas>
|
||||
|
||||
Our type system includes a dedicated language to define custom types that can be used in Python code. This language is hereafter referred to as the Midas language, or simply Midas. Its goal is to allow users to concisely define types, aliases, subtypes and the like in a simple and intuitive language. All syntax rules are listed in @tab:midas-syntax-rules.
|
||||
|
||||
#figure(
|
||||
midas-syntax,
|
||||
caption: [Midas syntax rules],
|
||||
supplement: [Table],
|
||||
kind: table,
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:midas-syntax-rules>
|
||||
|
||||
Parameter specifications in function types and predicates are simplified here for conciseness. In actuality, the supported syntax is similar to Python's regarding positional-only, keyword-only and mixed arguments, with the following notable differences:
|
||||
@@ -120,13 +120,13 @@ A more detailed definition of the syntax is available in @app:midas-syntax.
|
||||
|
||||
Some elements are worth explaining here. Midas offers four base statements through four keywords: `alias`, `type`, `extend` and `predicate`.\
|
||||
The first is rather straightforward: ```midas alias X = T``` defines an alias named `X` for the type `T`.\
|
||||
The second is a simple subtype definition. ```midas type X = T``` is similar to an alias, but it creates a subtype relationship instead of an equivalence. `X` is thus a subtype of `T`. This also allows defining generic types using type parameters, bounded or not, which can be referenced in the right-hand side of the definition. For example ```midas type Foo[T <: U] = list[T]``` defines a type generic `Foo` with one parameter `T` subtype of `U`, which is a subtype of `list[T]`.\
|
||||
The second is a simple subtype definition. ```midas type X = T``` is similar to an alias, but it creates a subtype relationship instead of an equivalence. `X` is thus a subtype of `T`. This also allows defining generic types using type parameters, bounded or not, which can be referenced in the right-hand side of the definition. For example ```midas type Foo[T <: U] = list[T]``` defines a generic type `Foo` with one parameter `T` subtype of `U`, which is a subtype of `list[T]`.\
|
||||
The third, `extend`, is linked to a subtype definition. It allows users to define members on a type, such as properties/attributes (`prop`) and methods (`def`). Methods may be overloaded to combine multiple signatures under the same name. The type specified after `extend` must obviously be defined.\
|
||||
The last kind of statement provides user with a way to define named constraint expressions and reuse them as functions in other constraint expressions. Predicates are defined like functions, with a list of parameters. This list may be split into multiple parameter specifications, for example ```midas predicate foo(a: float)(b: int) = a < b``` to allow partial application.
|
||||
The last kind of statement provides users with a way to define named constraint expressions and reuse them as functions in other constraint expressions. Predicates are defined like functions, with a list of parameters. This list may be split into multiple parameter specifications, for example ```midas predicate foo(a: float)(b: int) = a < b``` to allow partial application.
|
||||
|
||||
A particularity of Midas are its constraint types. A constraint type `T where e` is built from a base type `T` and a constraint expression `e`. It allows binding a constraint on the values belonging to this type, which will be materialized by assertions at runtime.
|
||||
|
||||
== Typing rules <sec:theory-typing>
|
||||
== Typing Rules <sec:theory-typing>
|
||||
|
||||
Now that we have defined what Python syntax we will support and our own definition language, we need to define how our type checker will be able to bind types to Python expressions. This section is heavily inspired by the _Pure simply typed lambda-calculus~($lambda_->$)_, _Simply typed lambda-calculus with subtyping~($lambda_(<:)$)_, _Polymorphic lambda-calculus (System F)_ and _Bounded quantification (kernel $F_(<:)$)_ presented in Chapters 9, 15, 23 and 26 of #acr("TaPL")@tapl.
|
||||
|
||||
@@ -137,8 +137,8 @@ First and foremost, we can define elementary typing rules for all literal values
|
||||
#figure(
|
||||
typing.literals,
|
||||
caption: [Typing rules: literals],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-literals>
|
||||
|
||||
Other constructs, although not constants, directly map to builtin types. These include literal lists, tuples and dictionaries. Each of these has a corresponding `list[T]`, `tuple` and `dict[K, V]` type. We will not define formal rules in this section regarding these elements, but a more in depth explanation will be given in the implementation of the type checker, in @sec:python-check-literals.
|
||||
@@ -152,8 +152,8 @@ Apart from literals, developers also need some building blocks to express their
|
||||
#figure(
|
||||
typing.expressions,
|
||||
caption: [Typing rules: expressions],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-expressions>
|
||||
|
||||
#smallcaps[T-Var] simply states that a variable can be typed iff it is in the context.
|
||||
@@ -175,8 +175,8 @@ Finally, expressions can be used in statements, which are special constructs tha
|
||||
#figure(
|
||||
typing.statements,
|
||||
caption: [Typing rules: statements],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-statements>
|
||||
|
||||
#smallcaps[T-Annot] is a rule which handles Python type annotations. It states that the type checker must take note of type hints in the context.
|
||||
@@ -200,36 +200,35 @@ Similarly to some calculi described in #acr("TaPL")@tapl and to allow flexibilit
|
||||
#figure(
|
||||
typing.subtyping,
|
||||
caption: [Typing rules: subtyping base],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-subtyping-base>
|
||||
|
||||
The subtyping relationship is by definition both reflective (#smallcaps[S-Refl]) and transitive (#smallcaps[S-Trans]).
|
||||
|
||||
The subtyping relationship is by definition both reflective (#smallcaps[S-Refl]) and transitive (#smallcaps[S-Trans]).\
|
||||
#smallcaps[T-Sub] is a consequence of the #acr("LSP"). A term $"t"$ of type $"S"$ can be used anywhere a term of type $"T"$ is expected if $"S"$ is a subtype of $"T"$.
|
||||
|
||||
In addition to these base rules, we need to define how functions, constraint types and generic types are handled.
|
||||
For constraint types, we will take the simple approach of considering only the base type for the subtype relationship. A more complete and interesting implementation could implement subtyping of constraint expression, where a condition such as `_ < 10` is a subtype of `_ < 100`, but the added complexity was deemed out of scope for this particular project. We do nonetheless define #smallcaps[S-Constr] in @tab:typing-subtyping-constraint to handle the base case.
|
||||
For constraint types, we will take the simple approach of considering only the base type for the subtype relationship. A more complete and interesting implementation could consider subtyping of constraint expression, where a condition such as `_ < 10` is a subtype of `_ < 100`, but the added complexity was deemed out of scope for this particular project. We do nonetheless define #smallcaps[S-Constr] in @tab:typing-subtyping-constraint to handle the base case.
|
||||
|
||||
#figure(
|
||||
typing.subtyping-constraint,
|
||||
caption: [Typing rules: subtyping constraint type],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-subtyping-constraint>
|
||||
|
||||
Generics are handled using similar rules to the _Bounded quantification (kernel $F_(<:)$)_ calculus presented in #acr("TaPL") Chapter 26@tapl. Additionally, Midas supports implicit variance, which is inferred from the locations in which type parameters are used, and is used when checking subtypes of applied generic types. A detailed explanation of variance inference is provided in the corresponding implementation section, @sec:variance-inference.
|
||||
Generics are handled using similar rules to the _Bounded quantification (kernel $F_(<:)$)_ calculus presented in #acr("TaPL") Chapter 26@tapl. Additionally, Midas supports implicit variance, which is inferred from the locations in which type parameters are referenced, and is used when checking subtypes of applied generic types. A detailed explanation of variance inference is provided in the corresponding implementation section, @sec:variance-inference.
|
||||
|
||||
Finally, functions in Python are complex types. The general subtyping rule is given in @tab:typing-subtyping-function, but the complete set of rules for checking parameter specifications is too complex to include here. It is however detailed in @app:function-subtyping.
|
||||
|
||||
#figure(
|
||||
typing.subtyping-function,
|
||||
caption: [Typing rules: subtyping functions],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-subtyping-function>
|
||||
|
||||
=== Special types <sec:theory-special-types>
|
||||
=== Special Types <sec:theory-special-types>
|
||||
|
||||
Two special types can be added to our type system for practical purposes. The first is a regular top type, as represented by `Any` in Python. Every type is a subtype of `Any`, as stated by #smallcaps[S-Any] in @tab:typing-subtyping-top.
|
||||
|
||||
@@ -238,8 +237,8 @@ In addition to `Any`, we will add a second top type to represent unknowns. When
|
||||
#figure(
|
||||
typing.subtyping-top,
|
||||
caption: [Typing rules: subtyping top types],
|
||||
kind: table,
|
||||
supplement: [Table],
|
||||
kind: "ruleset",
|
||||
supplement: [Ruleset]
|
||||
) <tab:typing-subtyping-top>
|
||||
|
||||
The special type `None`, also later referred to as `UnitType`, is used in Midas as a unit type, mapping Python functions' behavior regarding implicit returns.
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
#let statements = (
|
||||
($#syntax[alias] "X" = "T"$, [_alias definition_]),
|
||||
($#syntax[type] "X" = "T"$, [_type definition_]),
|
||||
($#syntax[type] "X"["P"^(i in 1..n)] = "T"$, [_generic definition_]),
|
||||
($#syntax[type] "X"["P"_i^(i in 1..n)] = "T"$, [_generic definition_]),
|
||||
($ cases(
|
||||
#syntax[extend] "X"["P"^(i in 1..n)] space \{,
|
||||
quad m^(j in 1..M),
|
||||
#syntax[extend] "X"["P"_i^(i in 1..n)] space \{,
|
||||
quad "m"_j^(j in 1..M),
|
||||
\},
|
||||
delim: "["
|
||||
) $, [_generic definition_]),
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#let statements = (
|
||||
($"t"$, [_expression_]),
|
||||
($"s"; "t"$, [_inline sequence_]),
|
||||
($"s"; "s"$, [_inline sequence_]),
|
||||
($ cases(
|
||||
"s" #nl,
|
||||
"s",
|
||||
@@ -36,14 +36,14 @@
|
||||
($"x": "T"$, [_variable declaration_]),
|
||||
($ cases(
|
||||
#syntax[def] f("x": "T") -> "T": #nl,
|
||||
#tab "t" #nl,
|
||||
#tab "s",
|
||||
delim: "["
|
||||
) $, [_def_]),
|
||||
($ cases(
|
||||
#syntax[if] "t": #nl,
|
||||
tab "s" #nl,
|
||||
#syntax[else]: #nl,
|
||||
tab "s" #nl,
|
||||
tab "s",
|
||||
delim: "["
|
||||
) $, [_if / else_]),
|
||||
($ cases(
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
= Architecture Overview <sec:impl-overview>
|
||||
|
||||
Our type checker is composed of several interdependent elements shown in @fig:architecture. At its core, there is a type registry which holds a list of all registered types. This registry is initialized with builtin types, like `float` and `str`, and can be further populated by user definitions written in the Midas language. Finally, it is used when type checking the Python source code based on the type checking rules we defined in @sec:theory-typing.
|
||||
Our type checker is composed of several interdependent elements shown in @fig:architecture. At its core, there is a types registry which holds a list of all registered types. This registry is initialized with builtin types, like `float` and `str`, and can be further populated by user definitions written in the Midas language. Finally, it is used when type checking the Python source code based on the type checking rules we defined in @sec:theory-typing.
|
||||
|
||||
#figure(
|
||||
architecture.overview,
|
||||
caption: [Implementation Architecture Overview],
|
||||
caption: [Implementation architecture overview],
|
||||
kind: image,
|
||||
supplement: [Figure],
|
||||
) <fig:architecture>
|
||||
|
||||
@@ -12,7 +12,7 @@ In our type system, we will indeed have different kinds of types with different
|
||||
== Base Types <sec:types-base-types>
|
||||
|
||||
The simplest types are those at the root of the type lattice with no or few properties. These include top-types like `Any`, `UnknownType` and `UnitType`/`None`, as described in @sec:theory-special-types, which can be represented as simple empty classes like in @fig:types-base-types. Builtin types also have a dedicated `BaseType` kind which holds the type's name.
|
||||
For the special `tuple` type, we can define a more specific class which can hold the each item's type. This will allow nice type checking of subscript expressions for example.
|
||||
For the special `tuple` type, we can define a more specific class which can hold each item's type. This will allow nice type checking of subscript expressions for example.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -50,7 +50,7 @@ A similar approach is used for constraint types, by embedding the base type and
|
||||
|
||||
To define generic types, we first need type variables, which are kinds of placeholder types. These type variables have a name, an optional bounding type and a variance. The latter can be represented as a simple enum. As explained in @sec:typing-subtyping, variance is inferred rather than explicitly annotated by the user. This inference process is described in @sec:variance-inference.
|
||||
Once type variables are defined, generic types are simply a kind of derived type with a list of parameters.
|
||||
We also need to define an application counterpart to represent a generic type that has been instantiated with some concrete type arguments. Here, the choice of having this `AppliedType` kind instead of simply using the generic's body with its parameters substituted has been made for multiple reasons. Most importantly, it allows diagnostics to be more specific and informative and eases some comparisons between different applied types.
|
||||
We also need to define an application counterpart to represent a generic type that has been instantiated with some concrete type arguments. Here, the choice of having this `AppliedType` kind instead of simply using the generic's body with its parameters substituted has been made for multiple reasons. Most importantly, it allows diagnostics to be more specific and informative, and eases some comparisons between different applied types.
|
||||
|
||||
These types are implemented by the classes in @fig:types-generics.
|
||||
|
||||
@@ -60,6 +60,7 @@ These types are implemented by the classes in @fig:types-generics.
|
||||
INVARIANT = "INVARIANT"
|
||||
COVARIANT = "COVARIANT"
|
||||
CONTRAVARIANT = "CONTRAVARIANT"
|
||||
|
||||
class TypeVar:
|
||||
name: str
|
||||
bound: Optional[Type]
|
||||
@@ -81,9 +82,9 @@ These types are implemented by the classes in @fig:types-generics.
|
||||
As noted in the theory chapter (@chap:theory), functions in Python are complex and highly flexible.
|
||||
A function type thus have a parameter specification and a return type. That parameter specification contains three kinds of parameters: positional-only (`pos`), keyword-only (`kw`) and mixed (`mixed`).
|
||||
@fig:types-functions shows the implemented structures to handle function types and their parameters.
|
||||
For simplicity, each parameter will store both its position and name, as well as its type and a flag indicating whether it is required or not (e.g. parameters with default values are optional). You may notice an additional `unsupported` flag in @fig:types-functions:10, which is added for some dataframe-related methods. More information shall be given in @sec:impl-python #todo[replace ref].
|
||||
For simplicity, each parameter will store both its position and name, as well as its type and a flag indicating whether it is required or not (e.g. parameters with default values are optional). You may notice an additional `unsupported` flag in @fig:types-functions:10, which is added for some dataframe-related methods.
|
||||
|
||||
Finally, we will also define a structure to hold multiple signature of an overloaded function because it cannot be represented as a single `Function`. You may also notice that `OverloadedFunction.overloads` does not store a list of `Function` but simply a list of `Type`, in @fig:types-functions:16. This allows the use of generic functions, which are `Function` types wrapped inside a `GenericType`.
|
||||
Finally, we will also define a structure to hold multiple signatures of an overloaded function because it cannot be represented as a single `Function`. You may also notice that `OverloadedFunction.overloads` does not store a list of `Function` but simply a list of `Type`, in @fig:types-functions:16. This allows the use of generic functions, which are `Function` types wrapped inside a `GenericType`.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -141,7 +142,7 @@ Although not types, predicates will also be represented by similar dataclasses i
|
||||
body: m.Expr
|
||||
alias: bool
|
||||
```,
|
||||
caption: [Predicate Dataclass]
|
||||
caption: [Predicate dataclass]
|
||||
) <fig:types-predicate>
|
||||
|
||||
This class holds three properties of predicates:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
= Types Registry <sec:impl-registry>
|
||||
|
||||
With structures to represent our types, we now need to implement the registry which hold all builtin and user-defined types, as well as named predicates. As a reminder of requirements given in @sec:impl-overview, the registry must also provide a method `is_subtype` to check whether according to our subtyping rules one type is subtype of another, a method to register a type with a name, a method to lookup a type given a name, and some methods to manipulate types, such as instantiating a generic type given some arguments. We will also registration and lookup methods for predicates, as well as for type members (defined in `extend` statements).
|
||||
With structures to represent our types, we now need to implement the registry which hold all builtin and user-defined types, as well as named predicates. As a reminder of requirements given in @sec:impl-overview, the registry must also provide a method `is_subtype` to check whether according to our subtyping rules one type is subtype of another, a method to register a type with a name, a method to lookup a type given a name, and some methods to manipulate types, such as instantiating a generic type given some arguments. We will also implement registration and lookup methods for predicates, as well as for type members (defined in `extend` statements).
|
||||
|
||||
@fig:registry-contract shows the minimal contract our registry must satisfy.
|
||||
|
||||
@@ -26,12 +26,12 @@ With structures to represent our types, we now need to implement the registry wh
|
||||
def lookup_member(self, type: Type, member_name: str) -> Optional[Type]: ...
|
||||
def lookup_predicate(self, name: str) -> Optional[Predicate]: ...
|
||||
```,
|
||||
caption: [Minimal Registry Contract]
|
||||
caption: [Minimal registry contract]
|
||||
) <fig:registry-contract>
|
||||
|
||||
The `define_*` and `lookup_*`/`get_*` methods are quite straightforward, inserting and querying an internal dictionary with some added safeguards and errors to prevent redefining an entity or handle undefined references. Additionally, `define_member` handles overriding methods by wrapping multiple method definitions with the same member name inside an `OverloadedFunction` type. This process is detailed in @sec:define_member hereafter. The complete implementation of `TypesRegistry` is available in the repository in #code-ref(<types-registry>, "midas/checker/registry.py")
|
||||
|
||||
== `define_member` <sec:define_member>
|
||||
== Implementing `define_member` <sec:define_member>
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -70,10 +70,10 @@ The `define_*` and `lookup_*`/`get_*` methods are quite straightforward, inserti
|
||||
caption: [Implementation of `TypesRegistry.define_member`]
|
||||
) <fig:define_member>
|
||||
|
||||
First in @fig:define_member:8, we retrieve the dictionary of all members defined for the given type, and check wether a member is already defined with the given name in @fig:define_member:9. If that is the case, we check whether the kind is equal; we avoid registering a method with the same name as a property and vice-versa. The current implementation simply ignores such definitions but an error could also be raised to be more explicit to the user.
|
||||
A second check in @fig:define_member:17 avoid redefining a member which is not a method (i.e. a property). If we are redefining a method, an `OverloadedFunction` is then built to combine already defined signatures with the new one, as implemented in @fig:define_member:24 to @fig:define_member:29.
|
||||
First in @fig:define_member:8, we retrieve the dictionary of all members defined for the given type, and check whether a member is already defined with the given name in @fig:define_member:9. If that is the case, we check whether the kind is equal; we avoid registering a method with the same name as a property and vice-versa. The current implementation simply ignores such definitions but an error could also be raised to be more explicit to the user.
|
||||
A second check in @fig:define_member:17 avoids redefining a member which is not a method (i.e. a property). If we are redefining a method, an `OverloadedFunction` is then built to combine already defined signatures with the new one, as implemented in @fig:define_member:24 to @fig:define_member:29.
|
||||
|
||||
== `lookup_member`
|
||||
== Implementing `lookup_member`
|
||||
|
||||
`lookup_member` also needs some logic to handle each kind ot types. Indeed, looking up a member on a type (e.g. when processing an attribute reference expression like `foo.bar`), it may be defined in any of the type's supertypes too. Thus the method needs to recurse in the hierarchy if the member cannot be found directly on the requested type.
|
||||
|
||||
@@ -127,9 +127,9 @@ A second check in @fig:define_member:17 avoid redefining a member which is not a
|
||||
caption: [Implementation of `TypesRegistry.lookup_member`]
|
||||
) <fig:lookup_member>
|
||||
|
||||
Because we defined our internal type representations with dataclasses, we can easily leverage Python's pattern-matching capabilities to handle each case, as shown in @fig:lookup_member. The method is also quite straightforward though two things can be noted. The first is that looking up a member on `UnknownType` returns `UnknownType`. This basically tells the registry to go along with any references to unknown types, trusting the user that what they are doing is correct. It also allows gracefully propagating errors to avoid early returns that could be caused by raising an exception. The second important part of `lookup_member` is how `AppliedType` is handled. Since this type is derived from a generic type, its members may contain type variables. These are substituted lazily when referenced, i.e. in this method. `substitute_typevars` is a method which performs this substitution recursively given a mapping of type parameters to concrete type arguments. Its implementation is available in the repository in #code-ref(<substitute_typevars>, "midas/checker/types.py").
|
||||
Because we defined our internal type representations with dataclasses, we can easily leverage Python's pattern-matching capabilities to handle each case, as shown in @fig:lookup_member. The method is also quite straightforward though two things can be noted. The first is that looking up a member on `UnknownType` returns `UnknownType`. This basically tells the registry to go along with any references to unknown types, trusting the user that what they are doing is correct. It also allows gracefully propagating errors to avoid early returns that could be caused by raising an exception. The second important part of `lookup_member` is how `AppliedType` is handled. Since this type is derived from a generic type, its members may contain type variables. These are substituted lazily when referenced, i.e. in this method. `substitute_typevars` is a function which performs this substitution recursively given a mapping of type parameters to concrete type arguments. Its implementation is available in the repository in #code-ref(<substitute_typevars>, "midas/checker/types.py").
|
||||
|
||||
== `apply_generic`
|
||||
== Implementing `apply_generic`
|
||||
|
||||
`apply_generic` is a simple method which builds an `AppliedType` from a `GenericType` and a list of type arguments. This method is used when processing expressions such as `Foo[Bar]` and is implemented using the aforementioned `substitute_typevars`. Moreover, it checks potential bounds of type parameters. It is also used for the special `TupleType` which is constructed as `tuple[Type1, Type2, ...]`, as shown in @fig:apply_generic.
|
||||
|
||||
@@ -185,7 +185,7 @@ The first case handling `DerivedType`, @fig:apply_generic:3 to @fig:apply_generi
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
== `is_subtype` <sec:is_subtype>
|
||||
== Implementing `is_subtype` <sec:is_subtype>
|
||||
|
||||
This method is one of the central parts of our type system. Its role is simple: judge, according to the rules we defined in @sec:typing-subtyping (and some we skipped), whether a given type (`type1`) should be considered a subtype of another (`type2`).
|
||||
|
||||
@@ -202,7 +202,7 @@ The trivial case where `type1` is the same as `type2` can be handled with a simp
|
||||
caption: [`is_subtype`: equal types]
|
||||
) <fig:is_subtype-equal>
|
||||
|
||||
Even if the types differ, some cases such as top types are still trivially handled. If `type2` is a top type, `type1` is by definition a subtype (#smallcaps[S-Any] and #smallcaps[S-Unknown] in @tab:typing-subtyping-top).
|
||||
Even if the types differ, some cases such as top types are still trivially handled as demonstrated in @fig:is_subtype-top. If `type2` is a top type, `type1` is by definition a subtype (#smallcaps[S-Any] and #smallcaps[S-Unknown] in @tab:typing-subtyping-top).
|
||||
|
||||
#codly(
|
||||
ranges: ((5, 10), (71, 71)),
|
||||
@@ -224,7 +224,7 @@ For builtin types, we define some hardcoded subtype relationships, which are che
|
||||
caption: [`is_subtype`: builtins and type variables]
|
||||
) <fig:is_subtype-base-vars>
|
||||
|
||||
Then we have two similar kinds of types. First, `DerivedType` which encode an explicit subtype relationship, and `ConstraintType` which contain a base type and a constraint. To check these, we simply call `is_subtype` on the base type recursively. The latter corresponds to #smallcaps[S-Constr] in @tab:typing-subtyping-constraint. Note that we only consider `type1`, and not `type2`. The reason we only recurse on the potential subtype is that we are checking whether `type2` is in the hierarchy _above_ `type1`, i.e. a supertype. Because we don't allow multiple inheritance, the type lattice is basically a tree (in the graph theory sense) and a supertype must be in the same branch. This recursion process is an application of #smallcaps[S-Trans] (see @tab:typing-subtyping-base), which should either converge at one of the simpler cases described above or converge at a negative result if the subtype relationship cannot be verified (i.e. not subtyping rule was matched).
|
||||
Then we have two similar kinds of types. First, `DerivedType` which encodes an explicit subtype relationship, and `ConstraintType` which contains a base type and a constraint. To check these, we simply call `is_subtype` on the base type recursively. The latter corresponds to #smallcaps[S-Constr] in @tab:typing-subtyping-constraint. Note that we only consider `type1`, and not `type2`. The reason we only recurse on the potential subtype is that we are checking whether `type2` is in the hierarchy _above_ `type1`, i.e. a supertype. Because we don't allow multiple inheritance, the type lattice is basically a tree (in the graph theory sense) and a supertype must be in the same branch. This recursion process is an application of #smallcaps[S-Trans] (see @tab:typing-subtyping-base), which should either converge at one of the simpler cases described above or converge at a negative result if the subtype relationship cannot be verified (i.e. not subtyping rule was matched).
|
||||
The implementation shown in @fig:is_subtype-derived-constr is a straightforward application of these rules.
|
||||
|
||||
#codly(
|
||||
@@ -237,7 +237,7 @@ The implementation shown in @fig:is_subtype-derived-constr is a straightforward
|
||||
) <fig:is_subtype-derived-constr>
|
||||
|
||||
Special care must be taken when comparing two `AppliedType`, that is, instances of generic types.
|
||||
For $"T"_1["A"_11, "A"_12, ...] <: "T"_2["A"_21, "A"_22, ...]$ to be true, $"T"_1$ and $"T"_2$ must first refer to the same generic type. If they are, we must then check that each pair of arguments $("A"_11, "A"_21), ("A"_12, "A"_22), ..$ respect the variance of corresponding type parameters. This means that for a given triplet $("A"_1, "A"_2, "P")$, where $"A"_1$ and $"A"_2$ are paired arguments of `type1` and `type2`, and $"P"$ is the matching type parameter of the generic type:
|
||||
For $"T"_1["A"_11, "A"_12, ...] <: "T"_2["A"_21, "A"_22, ...]$ to be true, $"T"_1$ and $"T"_2$ must first refer to the same generic type. If they do, we must then check that each pair of arguments $("A"_11, "A"_21), ("A"_12, "A"_22), ...$ respects the variance of corresponding type parameters. This means that for a given triplet $("A"_1, "A"_2, "P")$, where $"A"_1$ and $"A"_2$ are paired arguments of `type1` and `type2`, and $"P"$ is the matching type parameter of the generic type:
|
||||
- if $"P"$ is _covariant_, we must check that $"A"_1 <: "A"_2$
|
||||
- if $"P"$ is _contravariant_, we must check that $"A"_2 <: "A"_1$
|
||||
- if $"P"$ is _invariant_, we must check both conditions
|
||||
@@ -261,7 +261,7 @@ Only three rules remain to check for dataframes, columns and most importantly fu
|
||||
Columns are simply regarded as invariant generic types. They are not implement using a regular `GenericType`/`AppliedType` because it makes many mechanisms much simpler to implement and reason about, especially regarding attributes and methods, which is worth making them a special construct.
|
||||
|
||||
For dataframes, another approach is taken: structural subtyping. What this means in our type checker is that a dataframe is considered a subtype of another if it has at least the same columns (by name) as the supertype, and they are compatible (i.e. columns of `type1` are subtypes of `type2`'s). This idea has some benefits and drawbacks.
|
||||
Starting with the negative, allowing `type1` to have more columns than `type2` could have a runtime impact. For example, if a function iterates over the columns or applies an operation which is not compatible with one of the extra columns, runtime behavior would not match the type checker's expectations. On the other hand, this allows much more flexibility for users who can pass whole dataframe to function which only need a subset of columns.
|
||||
Starting with the negative, allowing `type1` to have more columns than `type2` could have a runtime impact. For example, if a function iterates over the columns or applies an operation which is not compatible with one of the extra columns, runtime behavior would not match the type checker's expectations. On the other hand, this allows much more flexibility for users who can pass a whole dataframe to functions which only need a subset of columns.
|
||||
A potential solution not explored in this work could be to add a dedicated type, construct or syntax for explicit structural subtyping.
|
||||
|
||||
Finally, as shown in @fig:is_subtype-df-cols-funcs, functions are checked separately. Indeed, the algorithm as described in @tab:typing-subtyping-function and @app:function-subtyping is too complex to inline in this method. The implementation is discussed in @sec:is_func_subtype.
|
||||
@@ -275,7 +275,7 @@ Finally, as shown in @fig:is_subtype-df-cols-funcs, functions are checked separa
|
||||
caption: [`is_subtype`: dataframes, columns and functions]
|
||||
) <fig:is_subtype-df-cols-funcs>
|
||||
|
||||
== `is_func_subtype` <sec:is_func_subtype>
|
||||
== Implementing `is_func_subtype` <sec:is_func_subtype>
|
||||
|
||||
This section describes the implementation of the function subtyping verification algorithm. Please refer to @app:function-subtyping for more information on the underlying theory and formal rules.
|
||||
The complete implementation of `is_func_subtype` is given in @fig:is_func_subtype.
|
||||
|
||||
@@ -16,13 +16,13 @@ Processing a Midas definitions file is done in 3 steps:
|
||||
|
||||
+ typing, i.e. processing each statement, registering types and predicates in the registry
|
||||
|
||||
Each of these steps map to a dedicated class, respectively `MidasLexer`, `MidasParser` and `MidasTyper`.
|
||||
Each of these steps maps to a dedicated class, respectively `MidasLexer`, `MidasParser` and `MidasTyper`.
|
||||
|
||||
== Lexing and parsing <sec:midas-lexing-parsing>
|
||||
== Lexing and Parsing <sec:midas-lexing-parsing>
|
||||
|
||||
The lexer and parser follow the structure presented by Nystrom@Nystrom2021 and implemented in #fn-link(<pebble-midas>, "https://git.kb28.ch/HEL/pebble", new: false)[pebble]@Pebble.
|
||||
|
||||
Concretely, the lexer scans the source code character by character and produces tokens. Several token types are defined to cover all needs in Midas, such as operators, identifiers, keywords and punctuation. The full list of token types is included in @tab:token-types. Whitespace and comments are also converted to tokens but are ignored by the parser. Each token, as represented by the class shown in @fig:token-class, has a token type, a lexeme (i.e. the raw characters making up that token) and a position. That position is crucial for reporting diagnostics as it allows precisely showing the user where an error is in the source file. Tokens representing literals also contain their literal value, like strings, numbers and constants.
|
||||
Concretely, the lexer scans the source code character by character and produces tokens. Several token types are defined to cover all needs in Midas, such as operators, identifiers, keywords and punctuation. The full list of token types is included in @tab:token-types. Whitespace and comments are also converted to tokens but are ignored by the parser. Each token, as represented by the class shown in @fig:token-class, has a token type, a lexeme (i.e. the raw characters making up that token) and a position. That position is crucial for reporting diagnostics as it allows precisely showing the user where an error is in the source file. Tokens representing literals also contain their literal values, like strings, numbers and other constants.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -140,7 +140,7 @@ These tokens are then passed through the parser which builds an #acr("AST") show
|
||||
|
||||
After passing through `MidasParser`, we thus obtain a stable and explicit structure that can be given to `MidasTyper` and interpreted (as far as a definition language can be interpreted).
|
||||
|
||||
== Processing statements <sec:midas-interpretation>
|
||||
== Processing Statements <sec:midas-interpretation>
|
||||
|
||||
Each statement is processed in order. In the following sections we will get in more details for each kind of statements, and look at how we can implement their effects on the types registry.
|
||||
|
||||
@@ -148,7 +148,7 @@ The complete implementation of `MidasTyper` is available in the project's reposi
|
||||
|
||||
The basic members and methods referenced in implementations of this section are listed in @fig:midas-typer-members.
|
||||
|
||||
=== Building types <sec:midas-building-types>
|
||||
=== Building Types <sec:midas-building-types>
|
||||
|
||||
Before we can register a new type or an alias, we must be able to convert an #acr("AST") representation of a type into our internal type representation structures. For this we leverage the visitor pattern by implementing the visitor class of `Type` #acr("AST") nodes, as generated in @sec:midas-lexing-parsing. We thus make `MidasTyper` extend ```python m.Type.Visitor[Type]```#footnote[To avoid confusion between Midas and Python #acr("AST") nodes, their namespaces are imported as `m` and `p` respectively. A standalone reference to `Type` relates to the union of internal type representations mentioned in @sec:impl-types.]<fn:types-ref>, indicating that it must handle visiting any `m.Type` node and return a `Type` object.
|
||||
|
||||
@@ -172,7 +172,7 @@ As an example, visiting a `m.NamedType` node simply looks up the name in the reg
|
||||
caption: [Midas Typer: implementation of `visit_named_type`]
|
||||
) <fig:midas-visit_named_type>
|
||||
|
||||
Applied generics are built by looking up the type by its name, processing all arguments to build types and call `TypesRegistry.apply_generic`. The implementation shown in @fig:midas-visit_generic_type also handles the special `Column` type separately, as discussed in @sec:is_subtype.
|
||||
Applied generics are built by looking up the type by its name, processing all arguments to build types and calling `TypesRegistry.apply_generic`. The implementation shown in @fig:midas-visit_generic_type also handles the special `Column` type separately, as discussed in @sec:is_subtype.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -225,7 +225,7 @@ As you can see in the implementation above, in @fig:midas-visit_constraint_type,
|
||||
|
||||
Processing functions and dataframe types is naturally implemented by processing their internal types (e.g. parameters, columns) and building the corresponding internal representations.
|
||||
|
||||
=== Type checking expressions <sec:midas-checking-expr>
|
||||
=== Type Checking Expressions <sec:midas-checking-expr>
|
||||
|
||||
The process of type checking constraint expressions (`m.Expr`) is nearly identical to type checking a Python expression. Thus, the implementation is quite similar. This will be covered in the section concerning `PythonTyper`, i.e. @sec:python-check-expr.
|
||||
For now, we only look at `visit_variable_expr` and `visit_wildcard_expr` which call `get_variable` to return the type of the referenced variable. This method is detailed in @fig:midas-typer-members and in essence does the following:
|
||||
@@ -235,7 +235,7 @@ For now, we only look at `visit_variable_expr` and `visit_wildcard_expr` which c
|
||||
|
||||
If all three lookups fail, `UnknownType` is returned and an error is reported.
|
||||
|
||||
=== Alias statement <sec:midas-alias-stmt>
|
||||
=== Alias Statement <sec:midas-alias-stmt>
|
||||
|
||||
Now that we can build types we must implement a way to register them for future reference.
|
||||
The first and simplest statement is the alias statement (`m.AliasStmt`). Its effect is to bind the given name to the type defined on the right-hand side. The implementation is quite straightforward, as demonstrated in @fig:midas-visit_alias_stmt. Do note that `self._current_name` is set before processing the `m.Type` node, and cleared afterwards, so that cyclic references can be caught. The exception raised by the registry is also caught and reported through an error diagnostic to continue processing remaining statements.
|
||||
@@ -255,10 +255,10 @@ The first and simplest statement is the alias statement (`m.AliasStmt`). Its eff
|
||||
caption: [Midas Typer: implementation of `visit_alias_stmt`]
|
||||
) <fig:midas-visit_alias_stmt>
|
||||
|
||||
=== Type statement <sec:midas-type-stmt>
|
||||
=== Type Statement <sec:midas-type-stmt>
|
||||
|
||||
Type statements work similarly to alias statements, though they wrap the defined type in a `DerivedType` or `GenericType` depending on whether there are parameters.
|
||||
Additionally, if parameters are given, they are processed and transformed into `TypeVar` instances, and registered in an internal `_local_variables` dictionary. This internal registry is used by `TypesRegistry.get_type` to resolve references to type variables inside generic types and `extend` statements.
|
||||
Additionally, if parameters are given, they are processed and transformed into `TypeVar` instances, and registered in an internal `_local_variables` dictionary, as shown in @fig:midas-visit_type_stmt. This internal registry is used by `TypesRegistry.get_type` to resolve references to type variables inside generic types and `extend` statements.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -268,10 +268,8 @@ Additionally, if parameters are given, they are processed and transformed into `
|
||||
params: list[TypeVar] = self._resolve_type_params(stmt.params)
|
||||
|
||||
type: Type = stmt.type.accept(self)
|
||||
if len(params) != 0:
|
||||
type = GenericType(name=name, params=params, body=type)
|
||||
else:
|
||||
type = DerivedType(name=name, type=type)
|
||||
if len(params) != 0: type = GenericType(name=name, params=params, body=type)
|
||||
else: type = DerivedType(name=name, type=type)
|
||||
try:
|
||||
self.types.define_type(name, type)
|
||||
except ValueError:
|
||||
@@ -293,7 +291,7 @@ Additionally, if parameters are given, they are processed and transformed into `
|
||||
caption: [Midas Typer: implementation of `visit_type_stmt`]
|
||||
) <fig:midas-visit_type_stmt>
|
||||
|
||||
=== Predicate statement <sec:midas-predicate-stmt>
|
||||
=== Predicate Statement <sec:midas-predicate-stmt>
|
||||
|
||||
When processing a predicate definition, we first need to gather all parameters and register them internally in `self._predicate_params`. Then we type check the predicate body, i.e. a constraint expression (`m.Expr`). After this we process all parameter specifications using the same method as for function types. Before defining the predicate, we also check that the body's type is valid, i.e. a subtype of `bool` or a function which returns a valid predicate (in case of a partially applied predicate). Lastly, we build a complete function signature for the predicate, combining curried parameter specifications into functions returning functions, and insert it in the registry. If the predicate definition does not include any parameter specification, we mark it as an alias. This whole procedure is implemented as shown in @fig:midas-visit_predicate_stmt.
|
||||
|
||||
@@ -307,7 +305,6 @@ When processing a predicate definition, we first need to gather all parameters a
|
||||
self._predicate_params[param.name.lexeme] = param.type.accept(self)
|
||||
type: Type = self.type_of(stmt.body)
|
||||
params: list[ParamSpec] = [self._visit_param_spec(spec) for spec in stmt.params]
|
||||
|
||||
if not self._is_valid_predicate(type):
|
||||
self.reporter.error(
|
||||
stmt.body.location,
|
||||
@@ -345,7 +342,7 @@ When processing a predicate definition, we first need to gather all parameters a
|
||||
caption: [Midas Typer: implementation of `visit_predicate_stmt`]
|
||||
) <fig:midas-visit_predicate_stmt>
|
||||
|
||||
=== Extend statement <sec:midas-extend-stmt>
|
||||
=== Extend Statement <sec:midas-extend-stmt>
|
||||
|
||||
The last kind of statement available in our definition language is the `extend` statement. As explained in @sec:theory-syntax-midas, this allows defining members (properties and methods) on an already defined type. Processing it is rather straightforward, and comes down to two steps. First, we need to get the type being extended from the registry. The second step is simply iterating over each member statement and calling `TypesRegistry.define_member`. @fig:midas-visit_extend_stmt presents the short implementation of the method handling `extend`statements.
|
||||
|
||||
@@ -394,7 +391,7 @@ Continuing with our example from @fig:example-midas-ast, the type statement woul
|
||||
caption: [Midas Example: registered type#footnote[Location and position objects are omitted for readability]]
|
||||
) <fig:example-midas-type>
|
||||
|
||||
== Variance inference <sec:variance-inference>
|
||||
== Variance Inference <sec:variance-inference>
|
||||
|
||||
The main `resolve` method of `MidasTyper`, as shown in @fig:midas-typer-resolve and @fig:midas-typer-members, first processes all statements, and then summons a `VarianceManager` to infer variance of all types.
|
||||
|
||||
@@ -413,8 +410,10 @@ By polarity, we mean that:
|
||||
- any _producer_ position, such as a function return type, is considered *positive*
|
||||
- any _consumer_ position, such as a function parameter, is considered *negative*
|
||||
|
||||
Algorithmically, when inferring the variance of a variable, we start at the root generic type with a positive polarity. The inference function identifies all possible positions (type body and members) and recurses with the current polarity multiplied by the position's polarity. For example, if the current polarity is positive and a variable is used in a producer position, the resulting polarity used when recursing is $+ times + = +$. If however the variable is used in a consumer position, the polarity becomes $+ times - = -$. A consumer or negative position basically _flips_ the current polarity. When the recursion reaches the bottom usage, i.e. a simple `TypeVar`, the polarity is recorded.
|
||||
This algorithm is implemented by the `VarianceInferrer` class in @fig:midas-variance-inferrer-walk.
|
||||
Algorithmically, when inferring the variance of a variable, we start at the root generic type with a positive polarity. The inference function identifies all possible positions (type body and members) and recurses with the current polarity multiplied by the position's polarity.
|
||||
|
||||
For example, if the current polarity is positive and a variable is used in a producer position, the resulting polarity used when recursing is $+ times + = +$. If however the variable is used in a consumer position, the polarity becomes $+ times - = -$. A consumer or negative position basically _flips_ the current polarity. When the recursion reaches the bottom usage, i.e. a simple `TypeVar`, the polarity is recorded.
|
||||
This algorithm is implemented by the `VarianceInferrer` class in @fig:midas-variance-inferrer-walk (omitted parts of the code are listed in @fig:midas-variance-inferrer).
|
||||
|
||||
#codly(
|
||||
ranges: (
|
||||
@@ -435,9 +434,9 @@ When the generic type has been full walked, the tracker returns the list of type
|
||||
- otherwise it is *invariant*
|
||||
Its implementation is given in @fig:midas-variance-tracker.
|
||||
|
||||
It should be noted that we also use a `VarianceManager` to orchestrate how all the types are processed. The naive approach is to simply iterate over each type and infer their variance. However, this breaks down when a type is referenced before it is processed, or even inside itself. To counter such situations, we introduce a queue of types currently being processed. When walking through a type, if we encounter an `AppliedType`, we first check whether it is in the queue. If not, we ask the manager to infer its variance before continuing, as highlighted in @fig:midas-variance-inferrer-walk:42 to @fig:midas-variance-inferrer-walk:47.
|
||||
It should be noted that we also use a `VarianceManager` to orchestrate how all the types are processed. The naive approach is to simply iterate over each type and infer its variance. However, this breaks down when a type is referenced before it has been processed, or even inside itself. To counter such situations, we introduce a queue of types currently being processed. When walking through a type, if we encounter an `AppliedType`, we first check whether it is in the queue. If not, we ask the manager to infer its variance before continuing, as highlighted in @fig:midas-variance-inferrer-walk:42 to @fig:midas-variance-inferrer-walk:47.
|
||||
|
||||
`VarianceManager` is a simple helper class. Its main method, `infer`, is shown in @fig:midas-variance-manager-infer.
|
||||
`VarianceManager` is thus a simple helper class. Its main method, `infer`, is shown in @fig:midas-variance-manager-infer. The complete class is given in @fig:midas-variance-manager.
|
||||
|
||||
#codly(
|
||||
range: (12, 21),
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
= Python Type Checking <sec:impl-python>
|
||||
|
||||
We have now built a type definition language and registry, but it is of no use if we cannot type-check our Python code. In a similar fashion to `MidasTyper`, we must now implement a `PythonTyper`. Its role is to take in some Python source code and the types registry, walk through each statement and expression, infer the type of each term and verify the program's soundness. We will need several tools to concretize this process, such as a resolver to keep track of which value each variable references, and an environment to store the inferred types of local variables.
|
||||
We have now built a type definition language and registry, but it is of no use if we cannot type check our Python code. In a similar fashion to `MidasTyper`, we must now implement a `PythonTyper`. Its role is to take in some Python source code and the types registry, walk through each statement and expression, infer the type of each term and verify the program's soundness. We will need several tools to concretize this process, such as a resolver to keep track of which value each variable references, and an environment to store the inferred types of local variables.
|
||||
|
||||
For the sake of readability and conciseness, many parts of the implementation will be omitted from the following sections, focusing instead on the basic principles and most interesting methods. As always, the full source code is available in the repository, mainly in #code-ref(<checker>, "midas/checker").
|
||||
|
||||
@@ -99,7 +99,7 @@ More generally, if all sub-branches assign to a variable, we can consider it def
|
||||
|
||||
== Environments <sec:python-env>
|
||||
|
||||
While type checking, we will need to record what each variable's type is, much like `Gamma` in formal typing rule definitions (see @sec:theory-typing). This leads to the implementation of an `Environment` class, as presented by Nystrom in chapter 8.3 of _Crafting Interpreters_@Nystrom2021. Mirroring `Resolver`'s scopes, environments are nested into one another to isolate code blocks. The main difference with Nystrom's `Environment` class is that ours will store types rather than values. Additionally, as demonstrated in @sec:python-returns, `Environment` is also responsible for keeping track of possible return types in the associated scope.
|
||||
While type checking, we will need to record what each variable's type is, much like $Gamma$ in formal typing rule definitions (see @sec:theory-typing). This leads to the implementation of an `Environment` class, as presented by Nystrom in chapter 8.3 of _Crafting Interpreters_@Nystrom2021. Mirroring `Resolver`'s scopes, environments are nested into one another to isolate code blocks. The main difference with Nystrom's `Environment` class is that ours will store types rather than values. Additionally, as demonstrated in @sec:python-returns, `Environment` is also responsible for keeping track of possible return types in the associated scope.
|
||||
|
||||
We also define a special `Preamble` environment which contains bindings for global variables in the Python language, such as builtin functions and type constructors.
|
||||
|
||||
@@ -110,7 +110,7 @@ The source code for `Environment` and `Preamble` is available in the repository
|
||||
We now have the tools to start type checking our Python code.
|
||||
The input given to `PythonTyper` is basically a sequence of statements (`p.Stmt`). Using the visitor pattern, we make `PythonTyper` extend ```python p.Stmt.Visitor[None]``` and implement all `visit_*_stmt` methods.
|
||||
|
||||
=== Variable assignment <sec:python-var-assign>
|
||||
=== Variable Assignment <sec:python-var-assign>
|
||||
|
||||
One of the main mechanics manipulating the environment is variable declaration and assignment.
|
||||
During parsing, a statement such as ```python foo: int = 3```, represented in Python as a single `ast.AnnAssign` node, is split into two #acr("AST") nodes. The first is a type assignment (`p.TypeAssign`), which declares a new variable with the given type. The second is a variable assignment (`p.AssignStmt`). Implementing `visit_type_assign` is straightforward: we first resolve the type annotation expression and then define a new variable in the current environment, as demonstrated in @fig:python-visit_type_assign.
|
||||
@@ -170,14 +170,14 @@ Handling the variable assignment part is a bit more involved, because Python (an
|
||||
caption: [Python Typer: implementation of `visit_assign_stmt` (variable target)]
|
||||
) <fig:python-visit_assign_stmt>
|
||||
|
||||
This is also one of the places our subtyping rules come into play. When assigning to an already defined variable, we check that the value's type is a subtype of the variable's type.#footnote[`PythonTyper.is_subtype` is simply a shortcut for calling `is_subtype` on the types registry], emitting an error diagnostics in case of incompatibility.
|
||||
This is also one of the places our subtyping rules come into play. When assigning to an already defined variable, we check that the value's type is a subtype of the variable's type.#footnote[`PythonTyper.is_subtype` is simply a shortcut for calling `is_subtype` on the types registry], emitting an error diagnostic in case of incompatibility.
|
||||
|
||||
=== Returns <sec:python-returns>
|
||||
|
||||
One special control flow statement is the ```py return``` keyword.
|
||||
Its effect is twofold. First, it cuts a branch short and any further statement in that branch is skipped. Secondly, it provides a possible return type for the parent function. The latter implies that return statements must appear inside functions.
|
||||
|
||||
To easily implement the first effect, we will take advantage of exceptions and raise one inside `visit_return_stmt` (named `ReturnException`). We must then catch this exception at the closest branching point, i.e. the entry point of a block. In our case, in addition to a method type checking a single statement (`process_stmt`), we can implement a method to type check a whole block (`process_block`). Inside the latter we can then wrap calls `process_stmt` in a `try`/`except` statement. If a `ReturnException` is caught while processing a block and some statements remain to be processed, we can emit a warning to the user indicating that they will be skipped at runtime.
|
||||
To easily implement the first effect, we will take advantage of exceptions and raise one inside `visit_return_stmt` (named `ReturnException`). We must then catch this exception at the closest branching point, i.e. the entry point of a block. In our case, in addition to a method type checking a single statement (`process_stmt`), we can implement a method to type check a whole block (`process_block`). Inside the latter we can then wrap calls to `process_stmt` in a `try`/`except` statement. If a `ReturnException` is caught while processing a block and some statements remain to be processed, we can emit a warning to the user indicating that they will be skipped at runtime.
|
||||
|
||||
@fig:python-visit_return_stmt shows how the exception is raised while @fig:python-process_block shows how the exception is caught.
|
||||
|
||||
@@ -219,7 +219,6 @@ Similarly to how we handled variable assignments in `if` branches (see @fig:reso
|
||||
```python
|
||||
def visit_if_stmt(self, stmt: p.IfStmt) -> None:
|
||||
test_type: Type = self.type_of(stmt.test)
|
||||
|
||||
if (
|
||||
not self.is_subtype(test_type, self.types.get_type("bool"))
|
||||
and test_type != UnknownType()
|
||||
@@ -238,7 +237,7 @@ Similarly to how we handled variable assignments in `if` branches (see @fig:reso
|
||||
caption: [Python Typer: implementation of `visit_if_stmt`]
|
||||
) <fig:python-visit_if_stmt>
|
||||
|
||||
=== Function definitions <sec:python-functions>
|
||||
=== Function Definitions <sec:python-functions>
|
||||
|
||||
Function definitions include many components from all kinds of parameters, default values return type hints and their bodies. One particular property is the optionality of return type hints and the potential multiplicity of return statements inside a function body.
|
||||
What we want for our type checker is to ensure soundness of return types while helping the user by giving them as much freedom as possible by inferring types where not explicitly given.
|
||||
@@ -270,7 +269,7 @@ The complete implementation of `PythonTyper.visit_function` is listed in @fig:py
|
||||
|
||||
== Type Checking Expressions <sec:python-check-expr>
|
||||
|
||||
We extend `PythonTyper` to also type check expressions by inheriting ```python p.Expr.Visitor[Type]``` and implementing `visit_*_expr` methods. We thus make all expression visitor methods return a `Type` instance. Indeed, each Python expression is a typeable element composed into other expressions or statements. Some expressions such as literal value are trivial to type check whilst others can be much more complex. The latter are detailed in separate sections.
|
||||
We extend `PythonTyper` to also type check expressions by inheriting ```python p.Expr.Visitor[Type]``` and implementing `visit_*_expr` methods. We thus make all expression visitor methods return a `Type` instance. Indeed, each Python expression is a typeable element composed into other expressions or statements. Some expressions such as literal values are trivial to type check whilst others can be much more complex. The latter are detailed in separate sections.
|
||||
|
||||
=== Literals <sec:python-check-literals>
|
||||
|
||||
@@ -292,7 +291,7 @@ Applying typing rules for literals (@tab:typing-literals) is only a matter of ma
|
||||
caption: [Python Typer: implementation of `visit_literal_expr`]
|
||||
) <fig:python-visit_literal_expr>
|
||||
|
||||
For literal list expressions, we will reuse `TypesRegistry.reduce_types` method used in @fig:python-function-return-type to try and find a supertype of all item types.
|
||||
For literal list expressions, we will reuse the `TypesRegistry.reduce_types` method used in @fig:python-function-return-type to try and find a supertype of all item types, as can be seen in @fig:python-visit_list_expr.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -321,7 +320,7 @@ Literal dictionaries are handled in the same manner, finding a supertype for key
|
||||
|
||||
In Python, builtin operators are implemented through _dunder-methods_, i.e. methods on the operands. To the eyes of the type checker, an operation is thus equivalent to a function call. We can implement this idea by following these steps:
|
||||
+ Get the dunder-method name corresponding to the operator
|
||||
+ Type-check all operands (two for binary operators, one for unary operators)
|
||||
+ Type check all operands (two for binary operators, one for unary operators)
|
||||
+ Lookup the method on the left operand#footnote[Reverse operations (e.g. `__radd__`) have not been implemented in Midas yet]
|
||||
+ Type check a call to the method with the given operand(s)
|
||||
|
||||
@@ -336,7 +335,7 @@ Similarly, subscripts are de-sugared into calls to `__getitem__`.
|
||||
|
||||
Function calls are not always as straightforward as matching arguments with parameters and getting the return type. For a start, the callee is not necessary a direct `Function`. It may be a `DerivedType` or an `AppliedType`, in which case we must _unfold_ the type hierarchy to get the actual function type. It may also be an `OverloadedFunction`, in which case we must resolve which overload to call. It can also be a `GenericType`, in which case we must try and resolve the actual type arguments based on usage. To keep the type checker tidy, we introduce a separate `CallDispatcher` class, which we can reuse in `MidasTyper` for call expressions too.
|
||||
|
||||
This dispatcher has one main entrypoint: `get_result`. This method, shown in @fig:dispatcher-get_result-signature, takes in a callee type, positional and keyword arguments, and returns a `CallResult` object. This object bundles information about the result type and potential error should the dispatcher fail to resolve the call. The complete methods discusses hereafter are included in @fig:call-dispatcher. Their implementations are available in the repository in #code-ref(<call-dispatcher>, "midas/checker/dispatcher.py").
|
||||
This dispatcher has one main entrypoint: `get_result`. This method, shown in @fig:dispatcher-get_result-signature, takes in a callee type, positional and keyword arguments, and returns a `CallResult` object. This object bundles information about the result type and potential error, should the dispatcher fail to resolve the call. The complete methods discussed hereafter are included in @fig:call-dispatcher. Their implementations are available in the repository in #code-ref(<call-dispatcher>, "midas/checker/dispatcher.py").
|
||||
|
||||
#figure(
|
||||
```python
|
||||
@@ -370,7 +369,7 @@ This dispatcher has one main entrypoint: `get_result`. This method, shown in @fi
|
||||
|
||||
`CallDispatcher` is generic in `E`, which represents an expression base class. This allows instantiating it both for `m.Expr` and `p.Expr`.
|
||||
|
||||
The simple case where the callee is a `Function` involves matching call-site arguments with parameters in the function's specification. Then, if a match is possible each pair is check for correct typing. Similar to variable assignment (#smallcaps[T-Assign], see @sec:python-var-assign), an argument must be of the same type as the parameter it matches with (module subsumption). Pragmatically, this corresponds to @fig:dispatcher-match-func.
|
||||
The simple case where the callee is a `Function` involves matching call-site arguments with parameters in the function's specification. Then, if a match is possible, each pair is checked for correct typing. Similar to variable assignment (#smallcaps[T-Assign], see @sec:python-var-assign), an argument must be of the same type as the parameter it matches with (modulo subsumption). Pragmatically, this corresponds to @fig:dispatcher-match-func.
|
||||
|
||||
#codly(
|
||||
range: (14, 24),
|
||||
@@ -381,7 +380,7 @@ The simple case where the callee is a `Function` involves matching call-site arg
|
||||
caption: [Call Dispatcher: call to `Function`]
|
||||
) <fig:dispatcher-match-func>
|
||||
|
||||
Some other simple cases mentioned above can be handled with recursion. Calls to `UnknownType` are also allowed by the type checker, only resulting in an `UnknownType` too. These form the three simple match-cases in @fig:dispatcher-match-simple.
|
||||
Some other simple cases mentioned above can be handled with recursion, as in @fig:dispatcher-match-simple. Calls to `UnknownType` are also allowed by the type checker, only resulting in an `UnknownType` too.
|
||||
|
||||
#codly(
|
||||
range: (25, 34),
|
||||
@@ -462,7 +461,7 @@ Now that we have a method to choose an overload given some arguments, we can add
|
||||
caption: [Call Dispatcher: call to `OverloadedFunction`]
|
||||
) <fig:dispatcher-match-overloaded>
|
||||
|
||||
=== Generic function <sec:python-call-generic>
|
||||
=== Generic Function <sec:python-call-generic>
|
||||
|
||||
Generic functions are useful for defining some kind of template behavior while allowing different types to be used. However, when it comes to a call to such a function, things get a bit more complicated. Indeed, type parameters must be mapped to concrete types and unified depending on the actual call-site arguments before getting the return type. Taking the example of a simple generic doubling function ```py def double(value: T) -> T```, the return type depends on the type of the argument. Furthermore, in a more complex case like ```py def add(v1: T, v2: T) -> T```, the type variable `T` must be mapped to the same type for both `v1` and `v2`.
|
||||
This process is covered more in depth in chapter 22 of #acr("TaPL")@tapl.
|
||||
@@ -483,7 +482,7 @@ Using this class, we can complete `get_result` with the last case handling calls
|
||||
|
||||
== Casts and Static Evaluation <sec:python-check-casts>
|
||||
|
||||
From a type checking point of view, cast expressions are trivial to implement by applying #smallcaps[T-Cast]. `visit_cast_expr` simply returns the type passed to `cast`. There is however a fundamental premise making casts sounds, that is, the expression must conform to the given type. Most of the time, users will use cast expressions when the expression is not properly typeable at compile-time, but will always be valid at runtime. To maintain type safety, we will thus generate a runtime assertion checking that premise (see @sec:gen-assertions). However, these check might be quite computationally expensive, so we will provide users with an unsafe alternative that does not produce any runtime verification, `unsafe_cast`.
|
||||
From a type checking point of view, cast expressions are trivial to implement by applying #smallcaps[T-Cast]. `visit_cast_expr` simply returns the type passed to `cast`. There is however a fundamental premise making casts sounds, that is, the expression must conform to the given type. Most of the time, users will use cast expressions when the expression is not properly typeable at compile-time, but will always be valid at runtime. To maintain type safety, we will thus generate a runtime assertion checking that premise (see @sec:gen-assertions). Still, these checks might be quite computationally expensive, so we will provide users with an unsafe alternative that does not produce any runtime verification, `unsafe_cast`.
|
||||
|
||||
Additionally, there is a particular category of expressions which can be checked statically to verify whether the cast expression is valid. These are all literal values. For example, given a ```midas type Positive = float where _ >= 0```, an expression such as ```py cast(Positive, -12.3)``` can be fully rejected at compile-time. Implementing such a verification is not as simple as it may seem, as it implies we must first extract the literal Python value from the #acr("AST") representation and _evaluate_ the cast. @fig:python-visit_cast_expr shows the full method type checking casts.
|
||||
|
||||
@@ -506,11 +505,11 @@ Additionally, there is a particular category of expressions which can be checked
|
||||
|
||||
=== Static Evaluator <sec:python-evaluator>
|
||||
|
||||
Most interestingly in the evaluation process is how to handle constraint types. In addition to checking compliance with the base type, the literal value must also fit the constraint expression, which means _evaluating_ the expression to a boolean. For this purpose we introduce a dedicate `Evaluator` class.
|
||||
Most interestingly in the evaluation process is how to handle constraint types. In addition to checking compliance with the base type, the literal value must also fit the constraint expression, which means _evaluating_ the expression to a boolean. For this purpose we introduce a dedicated `Evaluator` class.
|
||||
Our constraint evaluator implements `m.Expr.Visitor[Any]`, accepting any expression and returning a final value.
|
||||
Another solution could have been to let the expression be interpreted by Python, using ```py exec``` or a similar function, but our constraint syntax is quite limited and slightly different, it seemed more practical to implement our own interpreter. This also allows sand-boxing the environment to the context we want and handle calls to other predicate functions.
|
||||
Another solution could have been to let the expression be interpreted by Python, using ```py exec``` or a similar function, but our constraint syntax being quite limited and slightly different, it seemed more practical to implement our own interpreter. This also allows sand-boxing the environment to the context we want and handle calls to other predicate functions.
|
||||
|
||||
The implementation is no so interesting (basically applying operators, getting attributes with `getattr`, etc.). The complete source code is available in the repository in #code-ref(<evaluator>, "midas/checker/evaluator.py").
|
||||
The implementation is not so interesting (basically applying operators, getting attributes with `getattr`, etc.). The complete source code is available in the repository in #code-ref(<evaluator>, "midas/checker/evaluator.py").
|
||||
|
||||
One particularity of Midas is how it allows curried application of predicates. Given the example in @fig:example-curried-application, we see that the first call results in a partially applied predicate function, while the second evaluates to a boolean.
|
||||
|
||||
@@ -562,19 +561,19 @@ In practice, we keep track of a scope dictionary containing all defined variable
|
||||
|
||||
== Frames and Columns <sec:python-df-cols>
|
||||
|
||||
There is still one big part of the type checker that we have not covered: dataframes and columns. Properly type checking dataframe operations is a never-ending rabbit hole. Libraries like `pandas` or `polars` provide a enormous amount of features, as methods and syntax sugars. They also have highly polymorphic functions, accepting operations with all kinds of values, such as multiplying a dataframe by a scalar, a list, a column or even another dataframe. The results of these operations may vary quite a lot depending on the operands, or sometimes on the parameters passed to some functions. As discussed in @chap:state-of-the-art, some libraries do try and make developers' lives a little better by providing some static type-checking like #fn-link(<strictly-typed-pandas-py>, "https://strictly-typed-pandas.readthedocs.io")[_Strictly Typed Pandas_], or runtime schema verification like #fn-link(<pandera-py>, "https://pandera.readthedocs.io")[_Pandera_]@niels_bantilan-proc-scipy-2020.
|
||||
There is still one big part of the type checker that we have not covered: dataframes and columns. Properly type checking dataframe operations is a never-ending rabbit hole. Libraries like `pandas` or `polars` provide a enormous amount of features, as methods and syntax sugars. They also have highly polymorphic functions, accepting operations with all kinds of values, such as multiplying a dataframe by a scalar, a list, a column or even another dataframe. The results of these operations may vary quite a lot depending on the operands, or sometimes on the parameters passed to some functions. As discussed in @chap:state-of-the-art, some libraries do try and make developers' lives a little better by providing some static type checking like #fn-link(<strictly-typed-pandas-py>, "https://strictly-typed-pandas.readthedocs.io")[_Strictly Typed Pandas_], or runtime schema verification like #fn-link(<pandera-py>, "https://pandera.readthedocs.io")[_Pandera_]@niels_bantilan-proc-scipy-2020.
|
||||
|
||||
Midas stands in between by providing some static type-checking for dataframe schemas and operations as well as generating runtime checks when a value is cast to such a type.
|
||||
We will explore in this section how we can not only provide type-checking for frame columns, for either accessing or assigning to them, but also handle some method calls with best effort inference of the resulting type.
|
||||
Midas stands in between by providing some static type checking for dataframe schemas and operations as well as generating runtime checks when a value is cast to such a type.
|
||||
We will explore in this section how we can not only provide type checking for frame columns, for either accessing or assigning to them, but also handle some method calls with best effort inference of the resulting type.
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
=== Schema Manipulation <sec:python-df-manager>
|
||||
|
||||
When `PythonTyper` encounters a `p.SubscriptExpr` where the object is a `DataFrameType`, either in a getter context or in an assignment, we defer the resolution to a dedicated `FrameManager` class. This manager's responsibility is to check that referenced columns exist in the schema (and return their types). When assigning to a dataframe, it also build a modified version of the schema to include new columns.
|
||||
When `PythonTyper` encounters a `p.SubscriptExpr` where the object is a `DataFrameType`, either in a getter context or in an assignment, we defer the resolution to a dedicated `FrameManager` class. This manager's responsibility is to check that referenced columns exist in the schema (and return their types). When assigning to a dataframe, it also builds a modified version of the schema to include new columns.
|
||||
|
||||
Method calls, caught in `PythonTyper` and forwarded to the manager, are handled by another class: `FrameMethodRegistry`.
|
||||
These classes allow a better separation of concern and a parallel implementation for frame and column types. Indeed, we will also implement a `ColumnManager` and a `ColumnMethodRegistry`.
|
||||
These classes allow a better separation of concerns and parallel implementations for frame and column types. Indeed, we will also create a `ColumnManager` and a `ColumnMethodRegistry`.
|
||||
|
||||
=== Method Registries <sec:python-df-methods>
|
||||
|
||||
@@ -632,20 +631,20 @@ When adding two columns together, we proceed in a similar way by looking up the
|
||||
Lastly, we must consider one particular edge case. Since columns can come from various places, they are not guaranteed to contain the same number of items. For `pandas` in particular, whenever a column or dataframe is used with another, the shortest is padded with nulls to match the longest length. This can lead to unexpected results which are totally hidden to the type checker. To counter such side-effects, we will enforce the condition that both operands must have the same length, through a runtime assertion.\
|
||||
Using the resulting schema, we build an ad-hoc `Function` which is passed to `CallDispatcher`. This allows reporting useful diagnostics for invalid arguments and reuses the machinery we already put in place for function calls. In our example, the function signature would be `fn(other: Any) -> Frame[a: float, b: <Unknown>, c: <Unknown>]`.
|
||||
|
||||
The second kind of methods we will handle is slightly more complex. Aggregation methods act column-wise and perform operations between the all the values of the same column, reducing it to a scalar. For dataframes, aggregation may result in a series of scalars (one for each column) or a single scalar value. For the scope of this project, direct aggregation on raw dataframes will result in a top type. This does not mean that we cannot type check _some_ aggregations however.
|
||||
The second kind of methods we will handle is slightly more complex. Aggregation methods act column-wise and perform operations between all values of the same column, reducing it to a scalar. For dataframes, aggregation may result in a series of scalars (one for each column) or a single scalar value. For the scope of this project, direct aggregation on raw dataframes will result in a top type. This does not mean that we cannot type check _some_ aggregations however.
|
||||
|
||||
Column aggregation does have a pretty stable and predictable output. Taking the example of the `mean` method, we do know that the mean of a series of number is computed by adding them together and dividing the total by the count. Fortunately, addition and division are both simple builtin operations that we already type check. We can thus define a formula, as a function of the column's item type, which "computes" the result type. For the `mean` method, this formula is ```py lambda t: ((t, "__add__", t), "__truediv__", "int")```, or more formally, $("T" + "T") \/"int"$#footnote[This formula does make the assumption that $"T" + "T"$ results in the same type as $"T" + "T" + ... + "T"$].
|
||||
|
||||
Some other aggregation methods have even simpler formulae, such as ```py median = lambda t: t```, but some may not be represented in this form, such as the `std` or `kurtosis` methods. These simply return a top type.
|
||||
|
||||
One last important construct that Midas supports is the `groupby` method. This method allows partitioning a column or dataframe in groups, according to some criteria, and compute some aggregations on each one. Recall @sec:types-dataframes where we defined special type kinds `FrameGroupBy` and `ColumnGroupBy`. We now use them as results of `DataFrameType.groupby` and `ColumnType.groupby` respectively, and implement dedicated method registries for each one. Their methods only include aggregation methods. This time, we can properly implement them for dataframes, because type-checking an aggregation method on a `FrameGroupBy` is equivalent to type-checking the method on `ColumnGroupBy` instances of each column, combining the result in a new `DataFrameType`. The actual type-checking computation is thus deferred to the other registry for `ColumnGroupBy` methods.
|
||||
One last important construct that Midas supports is the `groupby` method. This method allows partitioning a column or dataframe in groups, according to some criteria, and compute some aggregations on each one. Recall @sec:types-dataframes where we defined special type kinds `FrameGroupBy` and `ColumnGroupBy`. We now use them as results of `DataFrameType.groupby` and `ColumnType.groupby` respectively, and implement dedicated method registries for each one. Their methods only include aggregation methods. This time, we can properly implement them for dataframes, because type checking an aggregation method on a `FrameGroupBy` is equivalent to type checking the method on `ColumnGroupBy` instances of each column, combining the result in a new `DataFrameType`. The actual type checking computation is thus deferred to the other registry for `ColumnGroupBy` methods.
|
||||
In turn this registry simply delegates to `ColumnMethodRegistry`.
|
||||
|
||||
All implementations of the classes discussed in this section are available in the repository in #code-ref(<df-methods>, "midas/checker/frames/").
|
||||
|
||||
== Output <sec:python-output>
|
||||
|
||||
We have finally finished implementing our type checker for Python. Although this report skips some parts of the implementation, most of the important areas are covered. Now while type-checking, we generated a number of diagnostics (warning or errors) which can be printed to the user. This is the job of the #acr("CLI"), which we will not discuss here but is available in the repository in #code-ref(<cli>, "midas/cli/"). Also checkout the #code-ref(<manual>, "docs/manual.pdf", body: [manual]) which provides detailed explanation of all #acr("CLI") functionalities.
|
||||
We have finally finished implementing our type checker for Python. Although this report skips some parts of the implementation, most of the important areas are covered. Now while type checking, we generated a number of diagnostics (warnings or errors) which can be printed to the user. This is the job of the #acr("CLI"), which we will not discuss here but is available in the repository in #code-ref(<cli>, "midas/cli/"). Also checkout the #code-ref(<manual>, "docs/manual.pdf", body: [manual]) which provides detailed explanations of all #acr("CLI") functionalities.
|
||||
|
||||
There is still one missing part which we will discuss in @sec:impl-generation to generate the runnable Python code, including any necessary runtime assertions.
|
||||
Our type checker must then output some information about what it checked and the potential assertions to generate, as for ensuring that two dataframes have the same length.
|
||||
|
||||
@@ -12,7 +12,7 @@ This process comes after type checking, which verified the program's soundness a
|
||||
|
||||
As Midas provides its own language to define custom types, the Python interpreter and other type checkers are completely oblivious to their existence and meaning. Although theoretically not necessary for runtime, types may be used while interpreting annotations, cast calls or through reflection. One solution is to generate stubs that can be used in Python code representing custom types defined in Midas.
|
||||
|
||||
Simple derived types are translated through class inheritance, type aliases using simple assignments, and generics with Python native `Generic` and `TypeVar` classes. Functions are converted to `Protocol` subclasses for rich structural subtyping with other type checkers.
|
||||
Simple derived types are translated through class inheritance, type aliases using simple assignments, and generics with Python's native `Generic` and `TypeVar` classes. Functions are converted to `Protocol` subclasses for rich structural subtyping with other type checkers.
|
||||
Obviously, not all features of Midas can be translated into Python. Constraint types for example are simply represented like derived types, omitting the constraint aspect.
|
||||
|
||||
The stubs can be manually generated with a #acr("CLI") command, to use when developing, and are automatically output when compiling for runtime use.
|
||||
|
||||
@@ -16,11 +16,11 @@ In @chap:theory and @chap:impl, we have theorized and implemented a complete typ
|
||||
|
||||
This is more than enough to make Midas usable in a wide range of contexts, including data science. The following sections demonstrate how Midas can be used to provide powerful type checking, both statically and at runtime.
|
||||
|
||||
== Weather pipeline example <sec:results-pipeline>
|
||||
== Weather Pipeline Example <sec:results-pipeline>
|
||||
|
||||
As an example, we will consider a sample weather-data transformation pipeline as shown in @app:example-pipeline, also available in the repository in #code-ref(<example-pipeline>, "examples/02_demonstration/weather/").
|
||||
As an example, we will consider a sample weather-data transformation pipeline as shown in @app:example-pipeline (@fig:example-pipeline-types and @fig:example-pipeline-code), also available in the repository in #code-ref(<example-pipeline>, "examples/02_demonstration/weather/").
|
||||
|
||||
=== Domain specific types <sec:pipeline-types>
|
||||
=== Domain Specific Types <sec:pipeline-types>
|
||||
|
||||
Using the Midas language, we are able to define domain-specific types, as demonstrated in @fig:pipeline-base-types.
|
||||
|
||||
@@ -59,9 +59,9 @@ Finally, we can concisely define frame schemas that will become useful when mani
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
=== Type checking in action <sec:pipeline-type-checking>
|
||||
=== Type Checking in Action <sec:pipeline-type-checking>
|
||||
|
||||
Now what is the time for Midas to shine. The first step in a transformation pipeline is load some data. We will used `pandas` to read a dataframe from a #acr("CSV") file. Now, the compiler or type checker has no idea what this #acr("CSV") file might look like. Even if we specify a schema, there is no guarantee that the runtime file will conform to it. At most, the type checker can say that the result of `read_csv` is a dataframe. We thus introduce a `cast` expression to actually tell the type checker what the dataframe contains. Furthermore, a runtime assertion will check that the value returned by `read_csv` does indeed match our expectations.
|
||||
Now is the time for Midas to shine. The first step in a transformation pipeline is to load some data. We will use `pandas` to read a dataframe from a #acr("CSV") file. Now, the compiler or type checker has no idea what this #acr("CSV") file might look like. Even if we specify a schema, there is no guarantee that the runtime file will conform to it. At most, the type checker can say that the result of `read_csv` is a dataframe. We thus introduce a `cast` expression to actually tell the type checker what the dataframe contains. Furthermore, a runtime assertion will check that the value returned by `read_csv` does indeed match our expectations.
|
||||
Our load function thus looks like @fig:pipeline-load.
|
||||
|
||||
#codly(
|
||||
@@ -85,7 +85,7 @@ In a second step, we might want to transform some values to more appropriate typ
|
||||
) <fig:pipeline-convert>
|
||||
|
||||
This does highlight two current weaknesses of Midas. The first is the need to copy the dataframe in @fig:pipeline-convert:16. This is due to the fact that Midas does not support reference types (see chapter 13 of #acr("TaPL")@tapl). This means the type checker is completely oblivious to the fact that modifying a dataframe somewhere might change other references in other places.
|
||||
The second issue, which is more of a possible optimization, is the fact that `cast` will re-check the whole dataframe at runtime, even though some checks are irrelevant given the parameter's type. This is made even more noticeable in @fig:pipeline-aggregation which will check base types again (e.g. `float`).
|
||||
The second issue, which is more of a possible optimization, is the fact that `cast` will re-check the whole dataframe at runtime, even though some checks are irrelevant given the parameter's type. This is made even more noticeable in @fig:pipeline-heat-index and @fig:pipeline-aggregation which will check base types again (e.g. `float`).
|
||||
|
||||
#codly(
|
||||
range: (26, 37),
|
||||
@@ -98,7 +98,7 @@ The second issue, which is more of a possible optimization, is the fact that `ca
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
As implemented in @sec:python-df-methods, Midas will type check many operations on dataframes and columns, including `groupby` and aggregation methods. The result the computation shown in @fig:pipeline-aggregation is already typed as a dataframe of `float` columns by the type checker. We only add a `cast` to bring back our domain specific types, while re-checking value constraints. This latter point reveals one great feature missing from Midas: constraint unification (this will be discussed in @chap:conclusion).
|
||||
As implemented in @sec:python-df-methods, Midas will type check many operations on dataframes and columns, including `groupby` and aggregation methods. The result of the computation shown in @fig:pipeline-aggregation is already typed as a dataframe of `float` columns by the type checker. We only add a `cast` to bring back our domain specific types, while re-checking value constraints. This latter point reveals one great feature missing from Midas: constraint unification (this will be discussed in @chap:conclusion).
|
||||
|
||||
#codly(
|
||||
range: (40, 53),
|
||||
@@ -109,7 +109,7 @@ As implemented in @sec:python-df-methods, Midas will type check many operations
|
||||
caption: [Example Pipeline: data aggregation]
|
||||
) <fig:pipeline-aggregation>
|
||||
|
||||
Finally, there are some cases where Midas is not capable of properly type-checking some expressions, either by choice of the user or because some syntax is not yet supported. In either case, the type checker will gracefully handle untyped values by propagating them as `UnknownType`. Not only is `UnknownType` a top-type, it will also accept any operation, attribute or subscript access, etc.
|
||||
Finally, there are some cases where Midas is not capable of properly type checking some expressions, either by choice of the user or because some syntax is not yet supported. In either case, the type checker will gracefully handle untyped values by propagating them as `UnknownType`. Not only is `UnknownType` a top-type, it will also accept any operation, attribute or subscript access, etc.
|
||||
|
||||
Moreover, users may want to cast an expression to a type but cannot afford the cost of checking it at runtime or feel it is too redundant with a previous known typing judgment. Alternatively, they may want to use a value with an unknown type which _behaves_ as another for all practical purposes (e.g. `np.float32`). In that case, they can use an escape hatch with `unsafe_cast` which blindly accepts that the given expression is of the specified type, as used in @fig:pipeline-plot.
|
||||
|
||||
@@ -124,7 +124,7 @@ Moreover, users may want to cast an expression to a type but cannot afford the c
|
||||
|
||||
#pagebreak(weak: true)
|
||||
|
||||
== Type errors <sec:results-errors>
|
||||
== Type Errors <sec:results-errors>
|
||||
|
||||
In the previous section, we focused on dataframe operations, casts and runtime type errors, but Midas also catches static type errors. One classical but sneaky kind of error is mixing incompatible units. While using millimeters instead of centimeters when 3D-printing a pen holder might be comical, mixing monetary currencies while handling enterprise assets will probably get you fired. As demonstrated in @fig:caught-errors, Midas can help you avoid this kind of errors that can happen when some values share the same base representation (`float`).
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
= Conclusion <chap:conclusion>
|
||||
|
||||
This chapter concludes our exploration of Midas. We have seen how to define some formal typing rules to create a stricter static type system for Python. We then developed a custom language to define custom types, including dependent types with value constraints. We implemented a parser for this language from scratch, following Nystrom's _Crafting Interpreters_@Nystrom2021 and adapting a previous Python implementation of such an interpreter, #fn-link(<pebble-conclusion>, "https://git.kb28.ch/HEL/pebble")[pebble]@Pebble, written by the student. We developed a type registry to store builtin and user-defined types as dataclasses. Then, we implemented a complete type checker which is capable of processing an important subset of Python, performing some definite assignment analysis, enforcing our formal typing and subtyping rules and reporting useful contextualized diagnostics to the user. It also provides some powerful type checking of dataframe schemas and operations, including data aggregations. Finally, we implemented a code generator which converts cast expressions into runtime assertions verifying dependent type constraints and other premises that cannot be checked statically.
|
||||
This chapter concludes our exploration of Midas. We have seen how to define some formal typing rules to create a stricter static type system for Python. We then developed a custom language to define custom types, including dependent types with value constraints. We implemented a parser for this language from scratch, following Nystrom's _Crafting Interpreters_@Nystrom2021 and adapting a previous Python implementation of such an interpreter, #fn-link(<pebble-conclusion>, "https://git.kb28.ch/HEL/pebble")[pebble]@Pebble, written by the student. We developed a types registry to store builtin and user-defined types as dataclasses. Then, we implemented a complete type checker which is capable of processing an important subset of Python, performing some definite assignment analysis, enforcing our formal typing and subtyping rules, and reporting useful contextualized diagnostics to the user. It also provides some powerful type checking of dataframe schemas and operations, including data aggregations. Finally, we implemented a code generator which converts cast expressions into runtime assertions verifying dependent type constraints and other premises that cannot be checked statically.
|
||||
|
||||
Reflecting on our initial objectives, we successfully created a type system on top of Python's type hints. It can detect many simple and complex type errors and generate runtime assertions when static checking is not sufficient. Midas respects Python's dynamic typing philosophy and leaves users free to annotate any amount of their code. Constraint types offer a flexible expression language to cover many use cases, from simple float value domain to calling builtin function or accessing object attributes.
|
||||
Reflecting on our initial objectives, we successfully created a type system on top of Python's type hints. It can detect many simple and complex type errors and generate runtime assertions when static checking is not sufficient. Midas respects Python's dynamic typing philosophy and leaves users free to annotate any amount of their code. Constraint types offer a flexible expression language to cover many use cases, from simple float value domain to calling builtin functions or accessing object attributes.
|
||||
|
||||
By leveraging the visitor pattern in the parsers and type checkers, Midas is also easy to extend. Adding support for more Python constructs is only a matter of parsing it into a dedicated #acr("AST") node and implementing visitor methods where necessary. Classes such as `CallDispatcher`, `TypesRegistry` and `Resolver` clearly separate concerns and help organize the complex architecture of our system.
|
||||
|
||||
@@ -18,7 +18,7 @@ A non-exhaustive list of such opportunities and ideas has been compiled and orga
|
||||
|
||||
=== Current Limitations <sec:current-limitations>
|
||||
|
||||
There are a few known limitations and unwanted behaviors in the current version of Midas. The first, which has already been discussed in @sec:gen-assertions, concerns `cast` inside logical expressions. Due to the way assertions are generated, as `assert` statements before the cast expression, the interpreter will eagerly evaluate them even if there value will be short-circuited by a logical operator. Another issue addressed in that section is the lack of runtime type checking of object members (properties and methods).
|
||||
There are a few known limitations and unwanted behaviors in the current version of Midas. The first, which has already been discussed in @sec:gen-assertions, concerns `cast` inside logical expressions. Due to the way assertions are generated, as `assert` statements before the cast expression, the interpreter will eagerly evaluate them even if their value will be short-circuited by a logical operator. Another issue addressed in that section is the lack of runtime type checking of object members (properties and methods).
|
||||
|
||||
The way method resolution is implemented also makes the behavior of operators slightly differ from what the interpreter will do at runtime. For example, reverse operators (e.g. `__radd__`) are not looked up by Midas. This makes some operations invalid for Midas, such as `1 + 1.0`, while `1.0 + 1` is correctly type checked.
|
||||
|
||||
@@ -37,7 +37,7 @@ One last important issue with Midas as it is, which would require some work to f
|
||||
Some Python features are only partially supported or have been deliberately left out of the scope. Nevertheless, a few could be implemented without developing major new infrastructure.
|
||||
One example of such a feature are argument sinks, allowing variadic function definitions.
|
||||
|
||||
There also a few builtin types and function which are missing from Midas' preamble. This includes the `Iterable` or `Sequence` types, currently replaced with `list` where needed. A number of builtin list-related functions also return some ad-hoc objects, like `range`, `filter`, `map` or `zip`.
|
||||
There are also a few builtin types and function which are missing from Midas' preamble. This includes the `Iterable` or `Sequence` types, currently replaced with `list` where needed. A number of builtin list-related functions also return some ad-hoc objects, like `range`, `filter`, `map` or `zip`.
|
||||
|
||||
Several syntax elements could also be added relatively easily, such as `while` loops, `break` and `continue` statements, lambdas, `else` clauses on `for` loops and decorators.
|
||||
|
||||
@@ -53,7 +53,7 @@ Finally, the following are some ideas for extension of Midas, including new feat
|
||||
|
||||
From a practical point of view, fully handling `import` statements would allow using Midas in more complex multi-file projects. This may allow distributing type definitions for packages too, similar to stubs.
|
||||
|
||||
Similar to how dataframes are handled, our type system could also handle structure such as `numpy` arrays, which are extremely common in data science in particular.
|
||||
Akin to how dataframes are handled, our type system could also support structures such as `numpy` arrays, which are extremely common in data science in particular.
|
||||
|
||||
Regarding constraint types, the current system could be improved by introducing a constraint solver capable of statically evaluating their domain and even allow subtyping rules such as $(x < 10) <: (x < 100)$.
|
||||
As mentioned in @sec:pipeline-type-checking, this could also lead to powerful type checking of aggregation methods by deriving domain constraints from the aggregation methods themselves. For example, the `mean` of positive numbers less than or equal to 100 is also a positive number less than or equal to 100.
|
||||
|
||||
@@ -11,9 +11,9 @@ En Python, la philosophie de _duck typing_ est souvent source d'erreurs, en part
|
||||
Midas est un nouveau système de types construit sur Python qui vise à offrir une vérification statique stricte et des assertions à l'exécution pour garantir l'intégrité des données.
|
||||
Midas inclut son propre langage de définition de types pour tous besoins spécifiques à un domaine.
|
||||
Il fournit également une vérification étendue des schémas et opérations sur les DataFrames Pandas, comme les méthodes d'agrégation.
|
||||
Les expression de casting sont vérifiée statiquement sur les valeurs littérales ou génèrent des assertions à l'exécution qui vérifient la conformité si elle ne peut pas être jugée à la compilation.
|
||||
L'entièreté du système est construit de zéro, à l'exception du parser pour le langage de définition qui est adapté d'un autre projet.
|
||||
Le système de type est dans un premier temps abordé d'un point de vu théorique, en puisant dans #acrfull("TaPL").
|
||||
Les expressions de casting sont vérifiées statiquement sur les valeurs littérales ou génèrent des assertions à l'exécution qui vérifient la conformité si elle ne peut pas être jugée à la compilation.
|
||||
L'ensemble du système est construit de zéro, à l'exception du parser pour le langage de définition qui est adapté d'un autre projet.
|
||||
Le système de types est dans un premier temps abordé d'un point de vu théorique, en puisant dans #acrfull("TaPL").
|
||||
L'implémentation est ensuite développée étape par étape, composant par composant.
|
||||
|
||||
/*
|
||||
|
||||
@@ -10,21 +10,118 @@
|
||||
|
||||
#import "@preview/isc-hei-exec-summary:0.8.1" : *
|
||||
#import "../meta.typ"
|
||||
#import "../report/figs/architecture.typ"
|
||||
#import "@local/codly:1.3.1": codly, codly-init, local
|
||||
#import "@preview/codly-languages:0.1.10": codly-languages
|
||||
|
||||
// Must be <= 365 characters long.
|
||||
#let summary = "Midas is cool!" // TODO
|
||||
#let summary = "Midas is a type system built on top of Python to provide both static type checking and runtime assertions. It includes its own type definition language, implements dependent types in the form of constraint expressions and offers extended checking of dataframe schemas and operations."
|
||||
|
||||
#assert(
|
||||
summary.len() <= 365,
|
||||
message: "Summary too long: len() > 365"
|
||||
)
|
||||
|
||||
#let content = [
|
||||
#todo[]
|
||||
#show: codly-init
|
||||
#codly(
|
||||
languages: codly-languages
|
||||
+ (
|
||||
midas: (
|
||||
name: "Midas",
|
||||
color: rgb("#eedd47"),
|
||||
icon: box(
|
||||
image(
|
||||
"../assets/icon.svg",
|
||||
height: 130%,
|
||||
fit: "contain",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#lorem-pars(500)
|
||||
#colbreak()
|
||||
#lorem-pars(500)
|
||||
#set raw(
|
||||
syntaxes: path("../midas.sublime-syntax")
|
||||
)
|
||||
#show raw.where(block: true): set text(size: 0.9em)
|
||||
|
||||
#let content = [
|
||||
= Objectives
|
||||
|
||||
Python is a very popular language known for its great flexibility. For beginners and many developers, its duck-typing philosophy is a great way to code quickly and without heavy syntax. However, it is extremely easy to mix things up, use the wrong variable or not realize that a value has the wrong type.
|
||||
|
||||
Additionally, in data science, most of the values handled by a program are only known at runtime, because they are loaded from a dataset or fetched from an external service.
|
||||
|
||||
Enter Midas, a new type system designed to allow developers to define custom types for their use case, check their script to avoid type errors and generate safe runtime code that will ensure data integrity. For data engineers, Midas also allows defining value constraints and will verify operations on Pandas DataFrames.
|
||||
|
||||
#codly(
|
||||
header: [types.midas]
|
||||
)
|
||||
#figure(
|
||||
```midas
|
||||
predicate not_empty(text: str) = len(text) != 0
|
||||
type Height = float where _ >= 0
|
||||
alias People = Frame[
|
||||
name: str where not_empty(_),
|
||||
height: Height
|
||||
]
|
||||
```,
|
||||
caption: [Example custom type definitions]
|
||||
)
|
||||
|
||||
= Explanation
|
||||
|
||||
Midas was developed in a modular fashion, adding features one-by-one, starting with the selection of a subset of the Python language to support and the design of a definition language. Then a parser was built to read in Midas files. After defining some formal typing rules, the first elements of the type checker were put in place to handle basic Python statements and expressions.
|
||||
|
||||
The type system was then extended to support subtyping, generics, methods and more. After implementing cast expressions, the code generator was developed and integrated in the compilation pipeline.
|
||||
|
||||
Once a strong foundation was laid out it was further strengthened with numerous other features, such as constraint types and type checking of dataframe operations. The latter allows powerful inference of some aggregation result types.
|
||||
|
||||
#codly(
|
||||
header: [script.py],
|
||||
highlights: (
|
||||
(
|
||||
line: 3,
|
||||
start: 11,
|
||||
tag: [#h(2pt)Column[Height]],
|
||||
fill: blue.lighten(60%)
|
||||
),
|
||||
(
|
||||
line: 4,
|
||||
start: 17,
|
||||
tag: [#h(2pt)Height],
|
||||
fill: blue.lighten(60%)
|
||||
),
|
||||
)
|
||||
)
|
||||
#figure(
|
||||
```python
|
||||
import pandas as pd
|
||||
people: People = cast(People, pd.read_csv(...))
|
||||
heights = people["height"]
|
||||
median_height = heights.median()
|
||||
```,
|
||||
caption: [Example type checking of dataframe operations]
|
||||
)
|
||||
|
||||
= Conclusion
|
||||
|
||||
This project provides a solid hybrid type system for Python, that helps data engineers and developers avoid costly mistakes. It is packed with many useful features such as runtime checking of cast expressions and constraint types. Its modular architecture makes it easily expandable too.
|
||||
|
||||
Some ideas for future extensions include adding a constraint solver, handling reference types and supporting multi-file projects through import statements.
|
||||
|
||||
#place(
|
||||
bottom + center,
|
||||
scope: "parent",
|
||||
float: true,
|
||||
figure(
|
||||
{
|
||||
set par(justify: false)
|
||||
architecture.overview
|
||||
},
|
||||
caption: [Implementation architecture overview]
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
#show: exec-summary.with(
|
||||
|
||||