diff --git a/.venv/lib/python3.8/site-packages/_cffi_backend.cpython-38-x86_64-linux-gnu.so b/.venv/lib/python3.8/site-packages/_cffi_backend.cpython-38-x86_64-linux-gnu.so
new file mode 100755
index 0000000000000000000000000000000000000000..9dbf7b30ba8dab1ced32c9e5a95cdf2c2692b71c
Binary files /dev/null and b/.venv/lib/python3.8/site-packages/_cffi_backend.cpython-38-x86_64-linux-gnu.so differ
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/INSTALLER b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/LICENSE b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..29225eee9edcd72c6a354550a5a3bedf1932b2ef
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/LICENSE
@@ -0,0 +1,26 @@
+
+Except when otherwise stated (look for LICENSE files in directories or
+information at the beginning of each file) all software and
+documentation is licensed as follows: 
+
+    The MIT License
+
+    Permission is hereby granted, free of charge, to any person 
+    obtaining a copy of this software and associated documentation 
+    files (the "Software"), to deal in the Software without 
+    restriction, including without limitation the rights to use, 
+    copy, modify, merge, publish, distribute, sublicense, and/or 
+    sell copies of the Software, and to permit persons to whom the 
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included 
+    in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+    DEALINGS IN THE SOFTWARE.
+
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/METADATA b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..60b0779f688341d4050c3b9aec494be135d2c468
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/METADATA
@@ -0,0 +1,40 @@
+Metadata-Version: 2.1
+Name: cffi
+Version: 1.17.1
+Summary: Foreign Function Interface for Python calling C code.
+Home-page: http://cffi.readthedocs.org
+Author: Armin Rigo, Maciej Fijalkowski
+Author-email: python-cffi@googlegroups.com
+License: MIT
+Project-URL: Documentation, http://cffi.readthedocs.org/
+Project-URL: Source Code, https://github.com/python-cffi/cffi
+Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues
+Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html
+Project-URL: Downloads, https://github.com/python-cffi/cffi/releases
+Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: License :: OSI Approved :: MIT License
+Requires-Python: >=3.8
+License-File: LICENSE
+Requires-Dist: pycparser
+
+
+CFFI
+====
+
+Foreign Function Interface for Python calling C code.
+Please see the `Documentation <http://cffi.readthedocs.org/>`_.
+
+Contact
+-------
+
+`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/RECORD b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..3cef4fe9d94bced03b4fb59d82bdc1efa8863dab
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/RECORD
@@ -0,0 +1,48 @@
+_cffi_backend.cpython-38-x86_64-linux-gnu.so,sha256=-WPkNMwzM40_EHyytSoFn6D7cZ8aBi7LT_Nk50hZPMI,998968
+cffi-1.17.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+cffi-1.17.1.dist-info/LICENSE,sha256=BLgPWwd7vtaICM_rreteNSPyqMmpZJXFh72W3x6sKjM,1294
+cffi-1.17.1.dist-info/METADATA,sha256=u6nuvP_qPJKu2zvIbi2zkGzVu7KjnnRIYUFyIrOY3j4,1531
+cffi-1.17.1.dist-info/RECORD,,
+cffi-1.17.1.dist-info/WHEEL,sha256=Ji6h3eq0qN8ihsFd7uspK43R6b4kEEoYhnwrY5S3EsU,147
+cffi-1.17.1.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75
+cffi-1.17.1.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19
+cffi/__init__.py,sha256=H6t_ebva6EeHpUuItFLW1gbRp94eZRNJODLaWKdbx1I,513
+cffi/__pycache__/__init__.cpython-38.pyc,,
+cffi/__pycache__/_imp_emulation.cpython-38.pyc,,
+cffi/__pycache__/_shimmed_dist_utils.cpython-38.pyc,,
+cffi/__pycache__/api.cpython-38.pyc,,
+cffi/__pycache__/backend_ctypes.cpython-38.pyc,,
+cffi/__pycache__/cffi_opcode.cpython-38.pyc,,
+cffi/__pycache__/commontypes.cpython-38.pyc,,
+cffi/__pycache__/cparser.cpython-38.pyc,,
+cffi/__pycache__/error.cpython-38.pyc,,
+cffi/__pycache__/ffiplatform.cpython-38.pyc,,
+cffi/__pycache__/lock.cpython-38.pyc,,
+cffi/__pycache__/model.cpython-38.pyc,,
+cffi/__pycache__/pkgconfig.cpython-38.pyc,,
+cffi/__pycache__/recompiler.cpython-38.pyc,,
+cffi/__pycache__/setuptools_ext.cpython-38.pyc,,
+cffi/__pycache__/vengine_cpy.cpython-38.pyc,,
+cffi/__pycache__/vengine_gen.cpython-38.pyc,,
+cffi/__pycache__/verifier.cpython-38.pyc,,
+cffi/_cffi_errors.h,sha256=zQXt7uR_m8gUW-fI2hJg0KoSkJFwXv8RGUkEDZ177dQ,3908
+cffi/_cffi_include.h,sha256=Exhmgm9qzHWzWivjfTe0D7Xp4rPUkVxdNuwGhMTMzbw,15055
+cffi/_embedding.h,sha256=EDKw5QrLvQoe3uosXB3H1xPVTYxsn33eV3A43zsA_Fw,18787
+cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960
+cffi/_shimmed_dist_utils.py,sha256=Bjj2wm8yZbvFvWEx5AEfmqaqZyZFhYfoyLLQHkXZuao,2230
+cffi/api.py,sha256=alBv6hZQkjpmZplBphdaRn2lPO9-CORs_M7ixabvZWI,42169
+cffi/backend_ctypes.py,sha256=h5ZIzLc6BFVXnGyc9xPqZWUS7qGy7yFSDqXe68Sa8z4,42454
+cffi/cffi_opcode.py,sha256=JDV5l0R0_OadBX_uE7xPPTYtMdmpp8I9UYd6av7aiDU,5731
+cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805
+cffi/cparser.py,sha256=0qI3mEzZSNVcCangoyXOoAcL-RhpQL08eG8798T024s,44789
+cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877
+cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584
+cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747
+cffi/model.py,sha256=W30UFQZE73jL5Mx5N81YT77us2W2iJjTm0XYfnwz1cg,21797
+cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976
+cffi/pkgconfig.py,sha256=LP1w7vmWvmKwyqLaU1Z243FOWGNQMrgMUZrvgFuOlco,4374
+cffi/recompiler.py,sha256=sim4Tm7lamt2Jn8uzKN0wMYp6ODByk3g7of47-h9LD4,65367
+cffi/setuptools_ext.py,sha256=-ebj79lO2_AUH-kRcaja2pKY1Z_5tloGwsJgzK8P3Cc,8871
+cffi/vengine_cpy.py,sha256=8UagT6ZEOZf6Dju7_CfNulue8CnsHLEzJYhnqUhoF04,43752
+cffi/vengine_gen.py,sha256=DUlEIrDiVin1Pnhn1sfoamnS5NLqfJcOdhRoeSNeJRg,26939
+cffi/verifier.py,sha256=oX8jpaohg2Qm3aHcznidAdvrVm5N4sQYG0a3Eo5mIl4,11182
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/WHEEL b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..8eae224895049c67caae293ed4e216ceb1a61d9f
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: setuptools (74.1.1)
+Root-Is-Purelib: false
+Tag: cp38-cp38-manylinux_2_17_x86_64
+Tag: cp38-cp38-manylinux2014_x86_64
+
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/entry_points.txt b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/entry_points.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4b0274f2333a8cfadbe2d13922c47d0138e48141
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[distutils.setup_keywords]
+cffi_modules = cffi.setuptools_ext:cffi_modules
diff --git a/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/top_level.txt b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f64577957eb0d893196994ae517759f3fa8e48dd
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi-1.17.1.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+_cffi_backend
+cffi
diff --git a/.venv/lib/python3.8/site-packages/cffi/__init__.py b/.venv/lib/python3.8/site-packages/cffi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e35a38c9ce02a703c5b2b4669d3d580776e19e0
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/__init__.py
@@ -0,0 +1,14 @@
+__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError',
+           'FFIError']
+
+from .api import FFI
+from .error import CDefError, FFIError, VerificationError, VerificationMissing
+from .error import PkgConfigError
+
+__version__ = "1.17.1"
+__version_info__ = (1, 17, 1)
+
+# The verifier module file names are based on the CRC32 of a string that
+# contains the following version number.  It may be older than __version__
+# if nothing is clearly incompatible.
+__version_verifier_modules__ = "0.8.6"
diff --git a/.venv/lib/python3.8/site-packages/cffi/_cffi_errors.h b/.venv/lib/python3.8/site-packages/cffi/_cffi_errors.h
new file mode 100644
index 0000000000000000000000000000000000000000..158e0590346a9a8b2ab047ac1bd23bcb3af21398
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/_cffi_errors.h
@@ -0,0 +1,149 @@
+#ifndef CFFI_MESSAGEBOX
+# ifdef _MSC_VER
+#  define CFFI_MESSAGEBOX  1
+# else
+#  define CFFI_MESSAGEBOX  0
+# endif
+#endif
+
+
+#if CFFI_MESSAGEBOX
+/* Windows only: logic to take the Python-CFFI embedding logic
+   initialization errors and display them in a background thread
+   with MessageBox.  The idea is that if the whole program closes
+   as a result of this problem, then likely it is already a console
+   program and you can read the stderr output in the console too.
+   If it is not a console program, then it will likely show its own
+   dialog to complain, or generally not abruptly close, and for this
+   case the background thread should stay alive.
+*/
+static void *volatile _cffi_bootstrap_text;
+
+static PyObject *_cffi_start_error_capture(void)
+{
+    PyObject *result = NULL;
+    PyObject *x, *m, *bi;
+
+    if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text,
+            (void *)1, NULL) != NULL)
+        return (PyObject *)1;
+
+    m = PyImport_AddModule("_cffi_error_capture");
+    if (m == NULL)
+        goto error;
+
+    result = PyModule_GetDict(m);
+    if (result == NULL)
+        goto error;
+
+#if PY_MAJOR_VERSION >= 3
+    bi = PyImport_ImportModule("builtins");
+#else
+    bi = PyImport_ImportModule("__builtin__");
+#endif
+    if (bi == NULL)
+        goto error;
+    PyDict_SetItemString(result, "__builtins__", bi);
+    Py_DECREF(bi);
+
+    x = PyRun_String(
+        "import sys\n"
+        "class FileLike:\n"
+        "  def write(self, x):\n"
+        "    try:\n"
+        "      of.write(x)\n"
+        "    except: pass\n"
+        "    self.buf += x\n"
+        "  def flush(self):\n"
+        "    pass\n"
+        "fl = FileLike()\n"
+        "fl.buf = ''\n"
+        "of = sys.stderr\n"
+        "sys.stderr = fl\n"
+        "def done():\n"
+        "  sys.stderr = of\n"
+        "  return fl.buf\n",   /* make sure the returned value stays alive */
+        Py_file_input,
+        result, result);
+    Py_XDECREF(x);
+
+ error:
+    if (PyErr_Occurred())
+    {
+        PyErr_WriteUnraisable(Py_None);
+        PyErr_Clear();
+    }
+    return result;
+}
+
+#pragma comment(lib, "user32.lib")
+
+static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored)
+{
+    Sleep(666);    /* may be interrupted if the whole process is closing */
+#if PY_MAJOR_VERSION >= 3
+    MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text,
+                L"Python-CFFI error",
+                MB_OK | MB_ICONERROR);
+#else
+    MessageBoxA(NULL, (char *)_cffi_bootstrap_text,
+                "Python-CFFI error",
+                MB_OK | MB_ICONERROR);
+#endif
+    _cffi_bootstrap_text = NULL;
+    return 0;
+}
+
+static void _cffi_stop_error_capture(PyObject *ecap)
+{
+    PyObject *s;
+    void *text;
+
+    if (ecap == (PyObject *)1)
+        return;
+
+    if (ecap == NULL)
+        goto error;
+
+    s = PyRun_String("done()", Py_eval_input, ecap, ecap);
+    if (s == NULL)
+        goto error;
+
+    /* Show a dialog box, but in a background thread, and
+       never show multiple dialog boxes at once. */
+#if PY_MAJOR_VERSION >= 3
+    text = PyUnicode_AsWideCharString(s, NULL);
+#else
+    text = PyString_AsString(s);
+#endif
+
+    _cffi_bootstrap_text = text;
+
+    if (text != NULL)
+    {
+        HANDLE h;
+        h = CreateThread(NULL, 0, _cffi_bootstrap_dialog,
+                         NULL, 0, NULL);
+        if (h != NULL)
+            CloseHandle(h);
+    }
+    /* decref the string, but it should stay alive as 'fl.buf'
+       in the small module above.  It will really be freed only if
+       we later get another similar error.  So it's a leak of at
+       most one copy of the small module.  That's fine for this
+       situation which is usually a "fatal error" anyway. */
+    Py_DECREF(s);
+    PyErr_Clear();
+    return;
+
+  error:
+    _cffi_bootstrap_text = NULL;
+    PyErr_Clear();
+}
+
+#else
+
+static PyObject *_cffi_start_error_capture(void) { return NULL; }
+static void _cffi_stop_error_capture(PyObject *ecap) { }
+
+#endif
diff --git a/.venv/lib/python3.8/site-packages/cffi/_cffi_include.h b/.venv/lib/python3.8/site-packages/cffi/_cffi_include.h
new file mode 100644
index 0000000000000000000000000000000000000000..908a1d7343f1869bc8818ca8e786f2c94a4732d2
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/_cffi_include.h
@@ -0,0 +1,389 @@
+#define _CFFI_
+
+/* We try to define Py_LIMITED_API before including Python.h.
+
+   Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and
+   Py_REF_DEBUG are not defined.  This is a best-effort approximation:
+   we can learn about Py_DEBUG from pyconfig.h, but it is unclear if
+   the same works for the other two macros.  Py_DEBUG implies them,
+   but not the other way around.
+
+   The implementation is messy (issue #350): on Windows, with _MSC_VER,
+   we have to define Py_LIMITED_API even before including pyconfig.h.
+   In that case, we guess what pyconfig.h will do to the macros above,
+   and check our guess after the #include.
+
+   Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv
+   version >= 16.0.0.  With older versions of either, you don't get a
+   copy of PYTHON3.DLL in the virtualenv.  We can't check the version of
+   CPython *before* we even include pyconfig.h.  ffi.set_source() puts
+   a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is
+   running on Windows < 3.5, as an attempt at fixing it, but that's
+   arguably wrong because it may not be the target version of Python.
+   Still better than nothing I guess.  As another workaround, you can
+   remove the definition of Py_LIMITED_API here.
+
+   See also 'py_limited_api' in cffi/setuptools_ext.py.
+*/
+#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
+#  ifdef _MSC_VER
+#    if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API)
+#      define Py_LIMITED_API
+#    endif
+#    include <pyconfig.h>
+     /* sanity-check: Py_LIMITED_API will cause crashes if any of these
+        are also defined.  Normally, the Python file PC/pyconfig.h does not
+        cause any of these to be defined, with the exception that _DEBUG
+        causes Py_DEBUG.  Double-check that. */
+#    ifdef Py_LIMITED_API
+#      if defined(Py_DEBUG)
+#        error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set"
+#      endif
+#      if defined(Py_TRACE_REFS)
+#        error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set"
+#      endif
+#      if defined(Py_REF_DEBUG)
+#        error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set"
+#      endif
+#    endif
+#  else
+#    include <pyconfig.h>
+#    if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API)
+#      define Py_LIMITED_API
+#    endif
+#  endif
+#endif
+
+#include <Python.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stddef.h>
+#include "parse_c_type.h"
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
+   and cffi/_cffi_include.h */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+#  ifndef __cplusplus
+    typedef unsigned char _Bool;
+#  endif
+# endif
+# define _cffi_float_complex_t   _Fcomplex    /* include <complex.h> for it */
+# define _cffi_double_complex_t  _Dcomplex    /* include <complex.h> for it */
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+# define _cffi_float_complex_t   float _Complex
+# define _cffi_double_complex_t  double _Complex
+#endif
+
+#ifdef __GNUC__
+# define _CFFI_UNUSED_FN  __attribute__((unused))
+#else
+# define _CFFI_UNUSED_FN  /* nothing */
+#endif
+
+#ifdef __cplusplus
+# ifndef _Bool
+   typedef bool _Bool;   /* semi-hackish: C++ has no _Bool; bool is builtin */
+# endif
+#endif
+
+/**********  CPython-specific section  **********/
+#ifndef PYPY_VERSION
+
+
+#if PY_MAJOR_VERSION >= 3
+# define PyInt_FromLong PyLong_FromLong
+#endif
+
+#define _cffi_from_c_double PyFloat_FromDouble
+#define _cffi_from_c_float PyFloat_FromDouble
+#define _cffi_from_c_long PyInt_FromLong
+#define _cffi_from_c_ulong PyLong_FromUnsignedLong
+#define _cffi_from_c_longlong PyLong_FromLongLong
+#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
+#define _cffi_from_c__Bool PyBool_FromLong
+
+#define _cffi_to_c_double PyFloat_AsDouble
+#define _cffi_to_c_float PyFloat_AsDouble
+
+#define _cffi_from_c_int(x, type)                                        \
+    (((type)-1) > 0 ? /* unsigned */                                     \
+        (sizeof(type) < sizeof(long) ?                                   \
+            PyInt_FromLong((long)x) :                                    \
+         sizeof(type) == sizeof(long) ?                                  \
+            PyLong_FromUnsignedLong((unsigned long)x) :                  \
+            PyLong_FromUnsignedLongLong((unsigned long long)x)) :        \
+        (sizeof(type) <= sizeof(long) ?                                  \
+            PyInt_FromLong((long)x) :                                    \
+            PyLong_FromLongLong((long long)x)))
+
+#define _cffi_to_c_int(o, type)                                          \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+                                         : (type)_cffi_to_c_i8(o)) :     \
+     sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
+                                         : (type)_cffi_to_c_i16(o)) :    \
+     sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o)       \
+                                         : (type)_cffi_to_c_i32(o)) :    \
+     sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
+                                         : (type)_cffi_to_c_i64(o)) :    \
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
+
+#define _cffi_to_c_i8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[1])
+#define _cffi_to_c_u8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[2])
+#define _cffi_to_c_i16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[3])
+#define _cffi_to_c_u16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[4])
+#define _cffi_to_c_i32                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[5])
+#define _cffi_to_c_u32                                                   \
+                 ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#define _cffi_to_c_i64                                                   \
+                 ((long long(*)(PyObject *))_cffi_exports[7])
+#define _cffi_to_c_u64                                                   \
+                 ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+#define _cffi_to_c_char                                                  \
+                 ((int(*)(PyObject *))_cffi_exports[9])
+#define _cffi_from_c_pointer                                             \
+    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10])
+#define _cffi_to_c_pointer                                               \
+    ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11])
+#define _cffi_get_struct_layout                                          \
+    not used any more
+#define _cffi_restore_errno                                              \
+    ((void(*)(void))_cffi_exports[13])
+#define _cffi_save_errno                                                 \
+    ((void(*)(void))_cffi_exports[14])
+#define _cffi_from_c_char                                                \
+    ((PyObject *(*)(char))_cffi_exports[15])
+#define _cffi_from_c_deref                                               \
+    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16])
+#define _cffi_to_c                                                       \
+    ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17])
+#define _cffi_from_c_struct                                              \
+    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18])
+#define _cffi_to_c_wchar_t                                               \
+    ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19])
+#define _cffi_from_c_wchar_t                                             \
+    ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20])
+#define _cffi_to_c_long_double                                           \
+    ((long double(*)(PyObject *))_cffi_exports[21])
+#define _cffi_to_c__Bool                                                 \
+    ((_Bool(*)(PyObject *))_cffi_exports[22])
+#define _cffi_prepare_pointer_call_argument                              \
+    ((Py_ssize_t(*)(struct _cffi_ctypedescr *,                           \
+                    PyObject *, char **))_cffi_exports[23])
+#define _cffi_convert_array_from_object                                  \
+    ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24])
+#define _CFFI_CPIDX  25
+#define _cffi_call_python                                                \
+    ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX])
+#define _cffi_to_c_wchar3216_t                                           \
+    ((int(*)(PyObject *))_cffi_exports[26])
+#define _cffi_from_c_wchar3216_t                                         \
+    ((PyObject *(*)(int))_cffi_exports[27])
+#define _CFFI_NUM_EXPORTS 28
+
+struct _cffi_ctypedescr;
+
+static void *_cffi_exports[_CFFI_NUM_EXPORTS];
+
+#define _cffi_type(index)   (                           \
+    assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \
+    (struct _cffi_ctypedescr *)_cffi_types[index])
+
+static PyObject *_cffi_init(const char *module_name, Py_ssize_t version,
+                            const struct _cffi_type_context_s *ctx)
+{
+    PyObject *module, *o_arg, *new_module;
+    void *raw[] = {
+        (void *)module_name,
+        (void *)version,
+        (void *)_cffi_exports,
+        (void *)ctx,
+    };
+
+    module = PyImport_ImportModule("_cffi_backend");
+    if (module == NULL)
+        goto failure;
+
+    o_arg = PyLong_FromVoidPtr((void *)raw);
+    if (o_arg == NULL)
+        goto failure;
+
+    new_module = PyObject_CallMethod(
+        module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg);
+
+    Py_DECREF(o_arg);
+    Py_DECREF(module);
+    return new_module;
+
+  failure:
+    Py_XDECREF(module);
+    return NULL;
+}
+
+
+#ifdef HAVE_WCHAR_H
+typedef wchar_t _cffi_wchar_t;
+#else
+typedef uint16_t _cffi_wchar_t;   /* same random pick as _cffi_backend.c */
+#endif
+
+_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o)
+{
+    if (sizeof(_cffi_wchar_t) == 2)
+        return (uint16_t)_cffi_to_c_wchar_t(o);
+    else
+        return (uint16_t)_cffi_to_c_wchar3216_t(o);
+}
+
+_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x)
+{
+    if (sizeof(_cffi_wchar_t) == 2)
+        return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
+    else
+        return _cffi_from_c_wchar3216_t((int)x);
+}
+
+_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o)
+{
+    if (sizeof(_cffi_wchar_t) == 4)
+        return (int)_cffi_to_c_wchar_t(o);
+    else
+        return (int)_cffi_to_c_wchar3216_t(o);
+}
+
+_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x)
+{
+    if (sizeof(_cffi_wchar_t) == 4)
+        return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
+    else
+        return _cffi_from_c_wchar3216_t((int)x);
+}
+
+union _cffi_union_alignment_u {
+    unsigned char m_char;
+    unsigned short m_short;
+    unsigned int m_int;
+    unsigned long m_long;
+    unsigned long long m_longlong;
+    float m_float;
+    double m_double;
+    long double m_longdouble;
+};
+
+struct _cffi_freeme_s {
+    struct _cffi_freeme_s *next;
+    union _cffi_union_alignment_u alignment;
+};
+
+_CFFI_UNUSED_FN static int
+_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg,
+                             char **output_data, Py_ssize_t datasize,
+                             struct _cffi_freeme_s **freeme)
+{
+    char *p;
+    if (datasize < 0)
+        return -1;
+
+    p = *output_data;
+    if (p == NULL) {
+        struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
+            offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
+        if (fp == NULL)
+            return -1;
+        fp->next = *freeme;
+        *freeme = fp;
+        p = *output_data = (char *)&fp->alignment;
+    }
+    memset((void *)p, 0, (size_t)datasize);
+    return _cffi_convert_array_from_object(p, ctptr, arg);
+}
+
+_CFFI_UNUSED_FN static void
+_cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
+{
+    do {
+        void *p = (void *)freeme;
+        freeme = freeme->next;
+        PyObject_Free(p);
+    } while (freeme != NULL);
+}
+
+/**********  end CPython-specific section  **********/
+#else
+_CFFI_UNUSED_FN
+static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *);
+# define _cffi_call_python  _cffi_call_python_org
+#endif
+
+
+#define _cffi_array_len(array)   (sizeof(array) / sizeof((array)[0]))
+
+#define _cffi_prim_int(size, sign)                                      \
+    ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8  : _CFFI_PRIM_UINT8)  :    \
+     (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) :    \
+     (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) :    \
+     (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) :    \
+     _CFFI__UNKNOWN_PRIM)
+
+#define _cffi_prim_float(size)                                          \
+    ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT :                       \
+     (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE :                     \
+     (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE :       \
+     _CFFI__UNKNOWN_FLOAT_PRIM)
+
+#define _cffi_check_int(got, got_nonpos, expected)      \
+    ((got_nonpos) == (expected <= 0) &&                 \
+     (got) == (unsigned long long)expected)
+
+#ifdef MS_WIN32
+# define _cffi_stdcall  __stdcall
+#else
+# define _cffi_stdcall  /* nothing */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/.venv/lib/python3.8/site-packages/cffi/_embedding.h b/.venv/lib/python3.8/site-packages/cffi/_embedding.h
new file mode 100644
index 0000000000000000000000000000000000000000..94d8b30a9e3c93a0ce3766f32dc291e09c8fff92
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/_embedding.h
@@ -0,0 +1,550 @@
+
+/***** Support code for embedding *****/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if defined(_WIN32)
+#  define CFFI_DLLEXPORT  __declspec(dllexport)
+#elif defined(__GNUC__)
+#  define CFFI_DLLEXPORT  __attribute__((visibility("default")))
+#else
+#  define CFFI_DLLEXPORT  /* nothing */
+#endif
+
+
+/* There are two global variables of type _cffi_call_python_fnptr:
+
+   * _cffi_call_python, which we declare just below, is the one called
+     by ``extern "Python"`` implementations.
+
+   * _cffi_call_python_org, which on CPython is actually part of the
+     _cffi_exports[] array, is the function pointer copied from
+     _cffi_backend.  If _cffi_start_python() fails, then this is set
+     to NULL; otherwise, it should never be NULL.
+
+   After initialization is complete, both are equal.  However, the
+   first one remains equal to &_cffi_start_and_call_python until the
+   very end of initialization, when we are (or should be) sure that
+   concurrent threads also see a completely initialized world, and
+   only then is it changed.
+*/
+#undef _cffi_call_python
+typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *);
+static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *);
+static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python;
+
+
+#ifndef _MSC_VER
+   /* --- Assuming a GCC not infinitely old --- */
+# define cffi_compare_and_swap(l,o,n)  __sync_bool_compare_and_swap(l,o,n)
+# define cffi_write_barrier()          __sync_synchronize()
+# if !defined(__amd64__) && !defined(__x86_64__) &&   \
+     !defined(__i386__) && !defined(__i386)
+#   define cffi_read_barrier()         __sync_synchronize()
+# else
+#   define cffi_read_barrier()         (void)0
+# endif
+#else
+   /* --- Windows threads version --- */
+# include <Windows.h>
+# define cffi_compare_and_swap(l,o,n) \
+                               (InterlockedCompareExchangePointer(l,n,o) == (o))
+# define cffi_write_barrier()       InterlockedCompareExchange(&_cffi_dummy,0,0)
+# define cffi_read_barrier()           (void)0
+static volatile LONG _cffi_dummy;
+#endif
+
+#ifdef WITH_THREAD
+# ifndef _MSC_VER
+#  include <pthread.h>
+   static pthread_mutex_t _cffi_embed_startup_lock;
+# else
+   static CRITICAL_SECTION _cffi_embed_startup_lock;
+# endif
+  static char _cffi_embed_startup_lock_ready = 0;
+#endif
+
+static void _cffi_acquire_reentrant_mutex(void)
+{
+    static void *volatile lock = NULL;
+
+    while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) {
+        /* should ideally do a spin loop instruction here, but
+           hard to do it portably and doesn't really matter I
+           think: pthread_mutex_init() should be very fast, and
+           this is only run at start-up anyway. */
+    }
+
+#ifdef WITH_THREAD
+    if (!_cffi_embed_startup_lock_ready) {
+# ifndef _MSC_VER
+        pthread_mutexattr_t attr;
+        pthread_mutexattr_init(&attr);
+        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+        pthread_mutex_init(&_cffi_embed_startup_lock, &attr);
+# else
+        InitializeCriticalSection(&_cffi_embed_startup_lock);
+# endif
+        _cffi_embed_startup_lock_ready = 1;
+    }
+#endif
+
+    while (!cffi_compare_and_swap(&lock, (void *)1, NULL))
+        ;
+
+#ifndef _MSC_VER
+    pthread_mutex_lock(&_cffi_embed_startup_lock);
+#else
+    EnterCriticalSection(&_cffi_embed_startup_lock);
+#endif
+}
+
+static void _cffi_release_reentrant_mutex(void)
+{
+#ifndef _MSC_VER
+    pthread_mutex_unlock(&_cffi_embed_startup_lock);
+#else
+    LeaveCriticalSection(&_cffi_embed_startup_lock);
+#endif
+}
+
+
+/**********  CPython-specific section  **********/
+#ifndef PYPY_VERSION
+
+#include "_cffi_errors.h"
+
+
+#define _cffi_call_python_org  _cffi_exports[_CFFI_CPIDX]
+
+PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void);   /* forward */
+
+static void _cffi_py_initialize(void)
+{
+    /* XXX use initsigs=0, which "skips initialization registration of
+       signal handlers, which might be useful when Python is
+       embedded" according to the Python docs.  But review and think
+       if it should be a user-controllable setting.
+
+       XXX we should also give a way to write errors to a buffer
+       instead of to stderr.
+
+       XXX if importing 'site' fails, CPython (any version) calls
+       exit().  Should we try to work around this behavior here?
+    */
+    Py_InitializeEx(0);
+}
+
+static int _cffi_initialize_python(void)
+{
+    /* This initializes Python, imports _cffi_backend, and then the
+       present .dll/.so is set up as a CPython C extension module.
+    */
+    int result;
+    PyGILState_STATE state;
+    PyObject *pycode=NULL, *global_dict=NULL, *x;
+    PyObject *builtins;
+
+    state = PyGILState_Ensure();
+
+    /* Call the initxxx() function from the present module.  It will
+       create and initialize us as a CPython extension module, instead
+       of letting the startup Python code do it---it might reimport
+       the same .dll/.so and get maybe confused on some platforms.
+       It might also have troubles locating the .dll/.so again for all
+       I know.
+    */
+    (void)_CFFI_PYTHON_STARTUP_FUNC();
+    if (PyErr_Occurred())
+        goto error;
+
+    /* Now run the Python code provided to ffi.embedding_init_code().
+     */
+    pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE,
+                              "<init code for '" _CFFI_MODULE_NAME "'>",
+                              Py_file_input);
+    if (pycode == NULL)
+        goto error;
+    global_dict = PyDict_New();
+    if (global_dict == NULL)
+        goto error;
+    builtins = PyEval_GetBuiltins();
+    if (builtins == NULL)
+        goto error;
+    if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0)
+        goto error;
+    x = PyEval_EvalCode(
+#if PY_MAJOR_VERSION < 3
+                        (PyCodeObject *)
+#endif
+                        pycode, global_dict, global_dict);
+    if (x == NULL)
+        goto error;
+    Py_DECREF(x);
+
+    /* Done!  Now if we've been called from
+       _cffi_start_and_call_python() in an ``extern "Python"``, we can
+       only hope that the Python code did correctly set up the
+       corresponding @ffi.def_extern() function.  Otherwise, the
+       general logic of ``extern "Python"`` functions (inside the
+       _cffi_backend module) will find that the reference is still
+       missing and print an error.
+     */
+    result = 0;
+ done:
+    Py_XDECREF(pycode);
+    Py_XDECREF(global_dict);
+    PyGILState_Release(state);
+    return result;
+
+ error:;
+    {
+        /* Print as much information as potentially useful.
+           Debugging load-time failures with embedding is not fun
+        */
+        PyObject *ecap;
+        PyObject *exception, *v, *tb, *f, *modules, *mod;
+        PyErr_Fetch(&exception, &v, &tb);
+        ecap = _cffi_start_error_capture();
+        f = PySys_GetObject((char *)"stderr");
+        if (f != NULL && f != Py_None) {
+            PyFile_WriteString(
+                "Failed to initialize the Python-CFFI embedding logic:\n\n", f);
+        }
+
+        if (exception != NULL) {
+            PyErr_NormalizeException(&exception, &v, &tb);
+            PyErr_Display(exception, v, tb);
+        }
+        Py_XDECREF(exception);
+        Py_XDECREF(v);
+        Py_XDECREF(tb);
+
+        if (f != NULL && f != Py_None) {
+            PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
+                               "\ncompiled with cffi version: 1.17.1"
+                               "\n_cffi_backend module: ", f);
+            modules = PyImport_GetModuleDict();
+            mod = PyDict_GetItemString(modules, "_cffi_backend");
+            if (mod == NULL) {
+                PyFile_WriteString("not loaded", f);
+            }
+            else {
+                v = PyObject_GetAttrString(mod, "__file__");
+                PyFile_WriteObject(v, f, 0);
+                Py_XDECREF(v);
+            }
+            PyFile_WriteString("\nsys.path: ", f);
+            PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0);
+            PyFile_WriteString("\n\n", f);
+        }
+        _cffi_stop_error_capture(ecap);
+    }
+    result = -1;
+    goto done;
+}
+
+#if PY_VERSION_HEX < 0x03080000
+PyAPI_DATA(char *) _PyParser_TokenNames[];  /* from CPython */
+#endif
+
+static int _cffi_carefully_make_gil(void)
+{
+    /* This does the basic initialization of Python.  It can be called
+       completely concurrently from unrelated threads.  It assumes
+       that we don't hold the GIL before (if it exists), and we don't
+       hold it afterwards.
+
+       (What it really does used to be completely different in Python 2
+       and Python 3, with the Python 2 solution avoiding the spin-lock
+       around the Py_InitializeEx() call.  However, after recent changes
+       to CPython 2.7 (issue #358) it no longer works.  So we use the
+       Python 3 solution everywhere.)
+
+       This initializes Python by calling Py_InitializeEx().
+       Important: this must not be called concurrently at all.
+       So we use a global variable as a simple spin lock.  This global
+       variable must be from 'libpythonX.Y.so', not from this
+       cffi-based extension module, because it must be shared from
+       different cffi-based extension modules.
+
+       In Python < 3.8, we choose
+       _PyParser_TokenNames[0] as a completely arbitrary pointer value
+       that is never written to.  The default is to point to the
+       string "ENDMARKER".  We change it temporarily to point to the
+       next character in that string.  (Yes, I know it's REALLY
+       obscure.)
+
+       In Python >= 3.8, this string array is no longer writable, so
+       instead we pick PyCapsuleType.tp_version_tag.  We can't change
+       Python < 3.8 because someone might use a mixture of cffi
+       embedded modules, some of which were compiled before this file
+       changed.
+
+       In Python >= 3.12, this stopped working because that particular
+       tp_version_tag gets modified during interpreter startup.  It's
+       arguably a bad idea before 3.12 too, but again we can't change
+       that because someone might use a mixture of cffi embedded
+       modules, and no-one reported a bug so far.  In Python >= 3.12
+       we go instead for PyCapsuleType.tp_as_buffer, which is supposed
+       to always be NULL.  We write to it temporarily a pointer to
+       a struct full of NULLs, which is semantically the same.
+    */
+
+#ifdef WITH_THREAD
+# if PY_VERSION_HEX < 0x03080000
+    char *volatile *lock = (char *volatile *)_PyParser_TokenNames;
+    char *old_value, *locked_value;
+
+    while (1) {    /* spin loop */
+        old_value = *lock;
+        locked_value = old_value + 1;
+        if (old_value[0] == 'E') {
+            assert(old_value[1] == 'N');
+            if (cffi_compare_and_swap(lock, old_value, locked_value))
+                break;
+        }
+        else {
+            assert(old_value[0] == 'N');
+            /* should ideally do a spin loop instruction here, but
+               hard to do it portably and doesn't really matter I
+               think: PyEval_InitThreads() should be very fast, and
+               this is only run at start-up anyway. */
+        }
+    }
+# else
+#  if PY_VERSION_HEX < 0x030C0000
+    int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag;
+    int old_value, locked_value = -42;
+    assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG));
+#  else
+    static struct ebp_s { PyBufferProcs buf; int mark; } empty_buffer_procs;
+    empty_buffer_procs.mark = -42;
+    PyBufferProcs *volatile *lock = (PyBufferProcs *volatile *)
+        &PyCapsule_Type.tp_as_buffer;
+    PyBufferProcs *old_value, *locked_value = &empty_buffer_procs.buf;
+#  endif
+
+    while (1) {    /* spin loop */
+        old_value = *lock;
+        if (old_value == 0) {
+            if (cffi_compare_and_swap(lock, old_value, locked_value))
+                break;
+        }
+        else {
+#  if PY_VERSION_HEX < 0x030C0000
+            assert(old_value == locked_value);
+#  else
+            /* The pointer should point to a possibly different
+               empty_buffer_procs from another C extension module */
+            assert(((struct ebp_s *)old_value)->mark == -42);
+#  endif
+            /* should ideally do a spin loop instruction here, but
+               hard to do it portably and doesn't really matter I
+               think: PyEval_InitThreads() should be very fast, and
+               this is only run at start-up anyway. */
+        }
+    }
+# endif
+#endif
+
+    /* call Py_InitializeEx() */
+    if (!Py_IsInitialized()) {
+        _cffi_py_initialize();
+#if PY_VERSION_HEX < 0x03070000
+        PyEval_InitThreads();
+#endif
+        PyEval_SaveThread();  /* release the GIL */
+        /* the returned tstate must be the one that has been stored into the
+           autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */
+    }
+    else {
+#if PY_VERSION_HEX < 0x03070000
+        /* PyEval_InitThreads() is always a no-op from CPython 3.7 */
+        PyGILState_STATE state = PyGILState_Ensure();
+        PyEval_InitThreads();
+        PyGILState_Release(state);
+#endif
+    }
+
+#ifdef WITH_THREAD
+    /* release the lock */
+    while (!cffi_compare_and_swap(lock, locked_value, old_value))
+        ;
+#endif
+
+    return 0;
+}
+
+/**********  end CPython-specific section  **********/
+
+
+#else
+
+
+/**********  PyPy-specific section  **********/
+
+PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]);   /* forward */
+
+static struct _cffi_pypy_init_s {
+    const char *name;
+    void *func;    /* function pointer */
+    const char *code;
+} _cffi_pypy_init = {
+    _CFFI_MODULE_NAME,
+    _CFFI_PYTHON_STARTUP_FUNC,
+    _CFFI_PYTHON_STARTUP_CODE,
+};
+
+extern int pypy_carefully_make_gil(const char *);
+extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *);
+
+static int _cffi_carefully_make_gil(void)
+{
+    return pypy_carefully_make_gil(_CFFI_MODULE_NAME);
+}
+
+static int _cffi_initialize_python(void)
+{
+    return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init);
+}
+
+/**********  end PyPy-specific section  **********/
+
+
+#endif
+
+
+#ifdef __GNUC__
+__attribute__((noinline))
+#endif
+static _cffi_call_python_fnptr _cffi_start_python(void)
+{
+    /* Delicate logic to initialize Python.  This function can be
+       called multiple times concurrently, e.g. when the process calls
+       its first ``extern "Python"`` functions in multiple threads at
+       once.  It can also be called recursively, in which case we must
+       ignore it.  We also have to consider what occurs if several
+       different cffi-based extensions reach this code in parallel
+       threads---it is a different copy of the code, then, and we
+       can't have any shared global variable unless it comes from
+       'libpythonX.Y.so'.
+
+       Idea:
+
+       * _cffi_carefully_make_gil(): "carefully" call
+         PyEval_InitThreads() (possibly with Py_InitializeEx() first).
+
+       * then we use a (local) custom lock to make sure that a call to this
+         cffi-based extension will wait if another call to the *same*
+         extension is running the initialization in another thread.
+         It is reentrant, so that a recursive call will not block, but
+         only one from a different thread.
+
+       * then we grab the GIL and (Python 2) we call Py_InitializeEx().
+         At this point, concurrent calls to Py_InitializeEx() are not
+         possible: we have the GIL.
+
+       * do the rest of the specific initialization, which may
+         temporarily release the GIL but not the custom lock.
+         Only release the custom lock when we are done.
+    */
+    static char called = 0;
+
+    if (_cffi_carefully_make_gil() != 0)
+        return NULL;
+
+    _cffi_acquire_reentrant_mutex();
+
+    /* Here the GIL exists, but we don't have it.  We're only protected
+       from concurrency by the reentrant mutex. */
+
+    /* This file only initializes the embedded module once, the first
+       time this is called, even if there are subinterpreters. */
+    if (!called) {
+        called = 1;  /* invoke _cffi_initialize_python() only once,
+                        but don't set '_cffi_call_python' right now,
+                        otherwise concurrent threads won't call
+                        this function at all (we need them to wait) */
+        if (_cffi_initialize_python() == 0) {
+            /* now initialization is finished.  Switch to the fast-path. */
+
+            /* We would like nobody to see the new value of
+               '_cffi_call_python' without also seeing the rest of the
+               data initialized.  However, this is not possible.  But
+               the new value of '_cffi_call_python' is the function
+               'cffi_call_python()' from _cffi_backend.  So:  */
+            cffi_write_barrier();
+            /* ^^^ we put a write barrier here, and a corresponding
+               read barrier at the start of cffi_call_python().  This
+               ensures that after that read barrier, we see everything
+               done here before the write barrier.
+            */
+
+            assert(_cffi_call_python_org != NULL);
+            _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org;
+        }
+        else {
+            /* initialization failed.  Reset this to NULL, even if it was
+               already set to some other value.  Future calls to
+               _cffi_start_python() are still forced to occur, and will
+               always return NULL from now on. */
+            _cffi_call_python_org = NULL;
+        }
+    }
+
+    _cffi_release_reentrant_mutex();
+
+    return (_cffi_call_python_fnptr)_cffi_call_python_org;
+}
+
+static
+void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args)
+{
+    _cffi_call_python_fnptr fnptr;
+    int current_err = errno;
+#ifdef _MSC_VER
+    int current_lasterr = GetLastError();
+#endif
+    fnptr = _cffi_start_python();
+    if (fnptr == NULL) {
+        fprintf(stderr, "function %s() called, but initialization code "
+                        "failed.  Returning 0.\n", externpy->name);
+        memset(args, 0, externpy->size_of_result);
+    }
+#ifdef _MSC_VER
+    SetLastError(current_lasterr);
+#endif
+    errno = current_err;
+
+    if (fnptr != NULL)
+        fnptr(externpy, args);
+}
+
+
+/* The cffi_start_python() function makes sure Python is initialized
+   and our cffi module is set up.  It can be called manually from the
+   user C code.  The same effect is obtained automatically from any
+   dll-exported ``extern "Python"`` function.  This function returns
+   -1 if initialization failed, 0 if all is OK.  */
+_CFFI_UNUSED_FN
+static int cffi_start_python(void)
+{
+    if (_cffi_call_python == &_cffi_start_and_call_python) {
+        if (_cffi_start_python() == NULL)
+            return -1;
+    }
+    cffi_read_barrier();
+    return 0;
+}
+
+#undef cffi_compare_and_swap
+#undef cffi_write_barrier
+#undef cffi_read_barrier
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/.venv/lib/python3.8/site-packages/cffi/_imp_emulation.py b/.venv/lib/python3.8/site-packages/cffi/_imp_emulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..136abdddf9d1276095e6f6724298ac19811c136a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/_imp_emulation.py
@@ -0,0 +1,83 @@
+
+try:
+    # this works on Python < 3.12
+    from imp import *
+
+except ImportError:
+    # this is a limited emulation for Python >= 3.12.
+    # Note that this is used only for tests or for the old ffi.verify().
+    # This is copied from the source code of Python 3.11.
+
+    from _imp import (acquire_lock, release_lock,
+                      is_builtin, is_frozen)
+
+    from importlib._bootstrap import _load
+
+    from importlib import machinery
+    import os
+    import sys
+    import tokenize
+
+    SEARCH_ERROR = 0
+    PY_SOURCE = 1
+    PY_COMPILED = 2
+    C_EXTENSION = 3
+    PY_RESOURCE = 4
+    PKG_DIRECTORY = 5
+    C_BUILTIN = 6
+    PY_FROZEN = 7
+    PY_CODERESOURCE = 8
+    IMP_HOOK = 9
+
+    def get_suffixes():
+        extensions = [(s, 'rb', C_EXTENSION)
+                      for s in machinery.EXTENSION_SUFFIXES]
+        source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES]
+        bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES]
+        return extensions + source + bytecode
+
+    def find_module(name, path=None):
+        if not isinstance(name, str):
+            raise TypeError("'name' must be a str, not {}".format(type(name)))
+        elif not isinstance(path, (type(None), list)):
+            # Backwards-compatibility
+            raise RuntimeError("'path' must be None or a list, "
+                               "not {}".format(type(path)))
+
+        if path is None:
+            if is_builtin(name):
+                return None, None, ('', '', C_BUILTIN)
+            elif is_frozen(name):
+                return None, None, ('', '', PY_FROZEN)
+            else:
+                path = sys.path
+
+        for entry in path:
+            package_directory = os.path.join(entry, name)
+            for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]:
+                package_file_name = '__init__' + suffix
+                file_path = os.path.join(package_directory, package_file_name)
+                if os.path.isfile(file_path):
+                    return None, package_directory, ('', '', PKG_DIRECTORY)
+            for suffix, mode, type_ in get_suffixes():
+                file_name = name + suffix
+                file_path = os.path.join(entry, file_name)
+                if os.path.isfile(file_path):
+                    break
+            else:
+                continue
+            break  # Break out of outer loop when breaking out of inner loop.
+        else:
+            raise ImportError(name, name=name)
+
+        encoding = None
+        if 'b' not in mode:
+            with open(file_path, 'rb') as file:
+                encoding = tokenize.detect_encoding(file.readline)[0]
+        file = open(file_path, mode, encoding=encoding)
+        return file, file_path, (suffix, mode, type_)
+
+    def load_dynamic(name, path, file=None):
+        loader = machinery.ExtensionFileLoader(name, path)
+        spec = machinery.ModuleSpec(name=name, loader=loader, origin=path)
+        return _load(spec)
diff --git a/.venv/lib/python3.8/site-packages/cffi/_shimmed_dist_utils.py b/.venv/lib/python3.8/site-packages/cffi/_shimmed_dist_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3d23128189fc121bff3f86b19330b0ac5ce0a45
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/_shimmed_dist_utils.py
@@ -0,0 +1,45 @@
+"""
+Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful
+error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken.
+
+This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI.
+"""
+import sys
+
+try:
+    # import setuptools first; this is the most robust way to ensure its embedded distutils is available
+    # (the .pth shim should usually work, but this is even more robust)
+    import setuptools
+except Exception as ex:
+    if sys.version_info >= (3, 12):
+        # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal
+        raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex
+
+    # silently ignore on older Pythons (support fallback to stdlib distutils where available)
+else:
+    del setuptools
+
+try:
+    # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils
+    from distutils import log, sysconfig
+    from distutils.ccompiler import CCompiler
+    from distutils.command.build_ext import build_ext
+    from distutils.core import Distribution, Extension
+    from distutils.dir_util import mkpath
+    from distutils.errors import DistutilsSetupError, CompileError, LinkError
+    from distutils.log import set_threshold, set_verbosity
+
+    if sys.platform == 'win32':
+        try:
+            # FUTURE: msvc9compiler module was removed in setuptools 74; consider removing, as it's only used by an ancient patch in `recompiler`
+            from distutils.msvc9compiler import MSVCCompiler
+        except ImportError:
+            MSVCCompiler = None
+except Exception as ex:
+    if sys.version_info >= (3, 12):
+        raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex
+
+    # anything older, just let the underlying distutils import error fly
+    raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex
+
+del sys
diff --git a/.venv/lib/python3.8/site-packages/cffi/api.py b/.venv/lib/python3.8/site-packages/cffi/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a474f3da9288e51df2ab93f7c40372955986265
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/api.py
@@ -0,0 +1,967 @@
+import sys, types
+from .lock import allocate_lock
+from .error import CDefError
+from . import model
+
+try:
+    callable
+except NameError:
+    # Python 3.1
+    from collections import Callable
+    callable = lambda x: isinstance(x, Callable)
+
+try:
+    basestring
+except NameError:
+    # Python 3.x
+    basestring = str
+
+_unspecified = object()
+
+
+
+class FFI(object):
+    r'''
+    The main top-level class that you instantiate once, or once per module.
+
+    Example usage:
+
+        ffi = FFI()
+        ffi.cdef("""
+            int printf(const char *, ...);
+        """)
+
+        C = ffi.dlopen(None)   # standard library
+        -or-
+        C = ffi.verify()  # use a C compiler: verify the decl above is right
+
+        C.printf("hello, %s!\n", ffi.new("char[]", "world"))
+    '''
+
+    def __init__(self, backend=None):
+        """Create an FFI instance.  The 'backend' argument is used to
+        select a non-default backend, mostly for tests.
+        """
+        if backend is None:
+            # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
+            # _cffi_backend.so compiled.
+            import _cffi_backend as backend
+            from . import __version__
+            if backend.__version__ != __version__:
+                # bad version!  Try to be as explicit as possible.
+                if hasattr(backend, '__file__'):
+                    # CPython
+                    raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r.  When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r.  The two versions should be equal; check your installation." % (
+                        __version__, __file__,
+                        backend.__version__, backend.__file__))
+                else:
+                    # PyPy
+                    raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r.  This interpreter comes with a built-in '_cffi_backend' module, which is version %s.  The two versions should be equal; check your installation." % (
+                        __version__, __file__, backend.__version__))
+            # (If you insist you can also try to pass the option
+            # 'backend=backend_ctypes.CTypesBackend()', but don't
+            # rely on it!  It's probably not going to work well.)
+
+        from . import cparser
+        self._backend = backend
+        self._lock = allocate_lock()
+        self._parser = cparser.Parser()
+        self._cached_btypes = {}
+        self._parsed_types = types.ModuleType('parsed_types').__dict__
+        self._new_types = types.ModuleType('new_types').__dict__
+        self._function_caches = []
+        self._libraries = []
+        self._cdefsources = []
+        self._included_ffis = []
+        self._windows_unicode = None
+        self._init_once_cache = {}
+        self._cdef_version = None
+        self._embedding = None
+        self._typecache = model.get_typecache(backend)
+        if hasattr(backend, 'set_ffi'):
+            backend.set_ffi(self)
+        for name in list(backend.__dict__):
+            if name.startswith('RTLD_'):
+                setattr(self, name, getattr(backend, name))
+        #
+        with self._lock:
+            self.BVoidP = self._get_cached_btype(model.voidp_type)
+            self.BCharA = self._get_cached_btype(model.char_array_type)
+        if isinstance(backend, types.ModuleType):
+            # _cffi_backend: attach these constants to the class
+            if not hasattr(FFI, 'NULL'):
+                FFI.NULL = self.cast(self.BVoidP, 0)
+                FFI.CData, FFI.CType = backend._get_types()
+        else:
+            # ctypes backend: attach these constants to the instance
+            self.NULL = self.cast(self.BVoidP, 0)
+            self.CData, self.CType = backend._get_types()
+        self.buffer = backend.buffer
+
+    def cdef(self, csource, override=False, packed=False, pack=None):
+        """Parse the given C source.  This registers all declared functions,
+        types, and global variables.  The functions and global variables can
+        then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
+        The types can be used in 'ffi.new()' and other functions.
+        If 'packed' is specified as True, all structs declared inside this
+        cdef are packed, i.e. laid out without any field alignment at all.
+        Alternatively, 'pack' can be a small integer, and requests for
+        alignment greater than that are ignored (pack=1 is equivalent to
+        packed=True).
+        """
+        self._cdef(csource, override=override, packed=packed, pack=pack)
+
+    def embedding_api(self, csource, packed=False, pack=None):
+        self._cdef(csource, packed=packed, pack=pack, dllexport=True)
+        if self._embedding is None:
+            self._embedding = ''
+
+    def _cdef(self, csource, override=False, **options):
+        if not isinstance(csource, str):    # unicode, on Python 2
+            if not isinstance(csource, basestring):
+                raise TypeError("cdef() argument must be a string")
+            csource = csource.encode('ascii')
+        with self._lock:
+            self._cdef_version = object()
+            self._parser.parse(csource, override=override, **options)
+            self._cdefsources.append(csource)
+            if override:
+                for cache in self._function_caches:
+                    cache.clear()
+            finishlist = self._parser._recomplete
+            if finishlist:
+                self._parser._recomplete = []
+                for tp in finishlist:
+                    tp.finish_backend_type(self, finishlist)
+
+    def dlopen(self, name, flags=0):
+        """Load and return a dynamic library identified by 'name'.
+        The standard C library can be loaded by passing None.
+        Note that functions and types declared by 'ffi.cdef()' are not
+        linked to a particular library, just like C headers; in the
+        library we only look for the actual (untyped) symbols.
+        """
+        if not (isinstance(name, basestring) or
+                name is None or
+                isinstance(name, self.CData)):
+            raise TypeError("dlopen(name): name must be a file name, None, "
+                            "or an already-opened 'void *' handle")
+        with self._lock:
+            lib, function_cache = _make_ffi_library(self, name, flags)
+            self._function_caches.append(function_cache)
+            self._libraries.append(lib)
+        return lib
+
+    def dlclose(self, lib):
+        """Close a library obtained with ffi.dlopen().  After this call,
+        access to functions or variables from the library will fail
+        (possibly with a segmentation fault).
+        """
+        type(lib).__cffi_close__(lib)
+
+    def _typeof_locked(self, cdecl):
+        # call me with the lock!
+        key = cdecl
+        if key in self._parsed_types:
+            return self._parsed_types[key]
+        #
+        if not isinstance(cdecl, str):    # unicode, on Python 2
+            cdecl = cdecl.encode('ascii')
+        #
+        type = self._parser.parse_type(cdecl)
+        really_a_function_type = type.is_raw_function
+        if really_a_function_type:
+            type = type.as_function_pointer()
+        btype = self._get_cached_btype(type)
+        result = btype, really_a_function_type
+        self._parsed_types[key] = result
+        return result
+
+    def _typeof(self, cdecl, consider_function_as_funcptr=False):
+        # string -> ctype object
+        try:
+            result = self._parsed_types[cdecl]
+        except KeyError:
+            with self._lock:
+                result = self._typeof_locked(cdecl)
+        #
+        btype, really_a_function_type = result
+        if really_a_function_type and not consider_function_as_funcptr:
+            raise CDefError("the type %r is a function type, not a "
+                            "pointer-to-function type" % (cdecl,))
+        return btype
+
+    def typeof(self, cdecl):
+        """Parse the C type given as a string and return the
+        corresponding <ctype> object.
+        It can also be used on 'cdata' instance to get its C type.
+        """
+        if isinstance(cdecl, basestring):
+            return self._typeof(cdecl)
+        if isinstance(cdecl, self.CData):
+            return self._backend.typeof(cdecl)
+        if isinstance(cdecl, types.BuiltinFunctionType):
+            res = _builtin_function_type(cdecl)
+            if res is not None:
+                return res
+        if (isinstance(cdecl, types.FunctionType)
+                and hasattr(cdecl, '_cffi_base_type')):
+            with self._lock:
+                return self._get_cached_btype(cdecl._cffi_base_type)
+        raise TypeError(type(cdecl))
+
+    def sizeof(self, cdecl):
+        """Return the size in bytes of the argument.  It can be a
+        string naming a C type, or a 'cdata' instance.
+        """
+        if isinstance(cdecl, basestring):
+            BType = self._typeof(cdecl)
+            return self._backend.sizeof(BType)
+        else:
+            return self._backend.sizeof(cdecl)
+
+    def alignof(self, cdecl):
+        """Return the natural alignment size in bytes of the C type
+        given as a string.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.alignof(cdecl)
+
+    def offsetof(self, cdecl, *fields_or_indexes):
+        """Return the offset of the named field inside the given
+        structure or array, which must be given as a C type name.
+        You can give several field names in case of nested structures.
+        You can also give numeric values which correspond to array
+        items, in case of an array type.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._typeoffsetof(cdecl, *fields_or_indexes)[1]
+
+    def new(self, cdecl, init=None):
+        """Allocate an instance according to the specified C type and
+        return a pointer to it.  The specified C type must be either a
+        pointer or an array: ``new('X *')`` allocates an X and returns
+        a pointer to it, whereas ``new('X[n]')`` allocates an array of
+        n X'es and returns an array referencing it (which works
+        mostly like a pointer, like in C).  You can also use
+        ``new('X[]', n)`` to allocate an array of a non-constant
+        length n.
+
+        The memory is initialized following the rules of declaring a
+        global variable in C: by default it is zero-initialized, but
+        an explicit initializer can be given which can be used to
+        fill all or part of the memory.
+
+        When the returned <cdata> object goes out of scope, the memory
+        is freed.  In other words the returned <cdata> object has
+        ownership of the value of type 'cdecl' that it points to.  This
+        means that the raw data can be used as long as this object is
+        kept alive, but must not be used for a longer time.  Be careful
+        about that when copying the pointer to the memory somewhere
+        else, e.g. into another structure.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.newp(cdecl, init)
+
+    def new_allocator(self, alloc=None, free=None,
+                      should_clear_after_alloc=True):
+        """Return a new allocator, i.e. a function that behaves like ffi.new()
+        but uses the provided low-level 'alloc' and 'free' functions.
+
+        'alloc' is called with the size as argument.  If it returns NULL, a
+        MemoryError is raised.  'free' is called with the result of 'alloc'
+        as argument.  Both can be either Python function or directly C
+        functions.  If 'free' is None, then no free function is called.
+        If both 'alloc' and 'free' are None, the default is used.
+
+        If 'should_clear_after_alloc' is set to False, then the memory
+        returned by 'alloc' is assumed to be already cleared (or you are
+        fine with garbage); otherwise CFFI will clear it.
+        """
+        compiled_ffi = self._backend.FFI()
+        allocator = compiled_ffi.new_allocator(alloc, free,
+                                               should_clear_after_alloc)
+        def allocate(cdecl, init=None):
+            if isinstance(cdecl, basestring):
+                cdecl = self._typeof(cdecl)
+            return allocator(cdecl, init)
+        return allocate
+
+    def cast(self, cdecl, source):
+        """Similar to a C cast: returns an instance of the named C
+        type initialized with the given 'source'.  The source is
+        casted between integers or pointers of any type.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.cast(cdecl, source)
+
+    def string(self, cdata, maxlen=-1):
+        """Return a Python string (or unicode string) from the 'cdata'.
+        If 'cdata' is a pointer or array of characters or bytes, returns
+        the null-terminated string.  The returned string extends until
+        the first null character, or at most 'maxlen' characters.  If
+        'cdata' is an array then 'maxlen' defaults to its length.
+
+        If 'cdata' is a pointer or array of wchar_t, returns a unicode
+        string following the same rules.
+
+        If 'cdata' is a single character or byte or a wchar_t, returns
+        it as a string or unicode string.
+
+        If 'cdata' is an enum, returns the value of the enumerator as a
+        string, or 'NUMBER' if the value is out of range.
+        """
+        return self._backend.string(cdata, maxlen)
+
+    def unpack(self, cdata, length):
+        """Unpack an array of C data of the given length,
+        returning a Python string/unicode/list.
+
+        If 'cdata' is a pointer to 'char', returns a byte string.
+        It does not stop at the first null.  This is equivalent to:
+        ffi.buffer(cdata, length)[:]
+
+        If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
+        'length' is measured in wchar_t's; it is not the size in bytes.
+
+        If 'cdata' is a pointer to anything else, returns a list of
+        'length' items.  This is a faster equivalent to:
+        [cdata[i] for i in range(length)]
+        """
+        return self._backend.unpack(cdata, length)
+
+   #def buffer(self, cdata, size=-1):
+   #    """Return a read-write buffer object that references the raw C data
+   #    pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
+   #    an array.  Can be passed to functions expecting a buffer, or directly
+   #    manipulated with:
+   #
+   #        buf[:]          get a copy of it in a regular string, or
+   #        buf[idx]        as a single character
+   #        buf[:] = ...
+   #        buf[idx] = ...  change the content
+   #    """
+   #    note that 'buffer' is a type, set on this instance by __init__
+
+    def from_buffer(self, cdecl, python_buffer=_unspecified,
+                    require_writable=False):
+        """Return a cdata of the given type pointing to the data of the
+        given Python object, which must support the buffer interface.
+        Note that this is not meant to be used on the built-in types
+        str or unicode (you can build 'char[]' arrays explicitly)
+        but only on objects containing large quantities of raw data
+        in some other format, like 'array.array' or numpy arrays.
+
+        The first argument is optional and default to 'char[]'.
+        """
+        if python_buffer is _unspecified:
+            cdecl, python_buffer = self.BCharA, cdecl
+        elif isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.from_buffer(cdecl, python_buffer,
+                                         require_writable)
+
+    def memmove(self, dest, src, n):
+        """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
+
+        Like the C function memmove(), the memory areas may overlap;
+        apart from that it behaves like the C function memcpy().
+
+        'src' can be any cdata ptr or array, or any Python buffer object.
+        'dest' can be any cdata ptr or array, or a writable Python buffer
+        object.  The size to copy, 'n', is always measured in bytes.
+
+        Unlike other methods, this one supports all Python buffer including
+        byte strings and bytearrays---but it still does not support
+        non-contiguous buffers.
+        """
+        return self._backend.memmove(dest, src, n)
+
+    def callback(self, cdecl, python_callable=None, error=None, onerror=None):
+        """Return a callback object or a decorator making such a
+        callback object.  'cdecl' must name a C function pointer type.
+        The callback invokes the specified 'python_callable' (which may
+        be provided either directly or via a decorator).  Important: the
+        callback object must be manually kept alive for as long as the
+        callback may be invoked from the C level.
+        """
+        def callback_decorator_wrap(python_callable):
+            if not callable(python_callable):
+                raise TypeError("the 'python_callable' argument "
+                                "is not callable")
+            return self._backend.callback(cdecl, python_callable,
+                                          error, onerror)
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl, consider_function_as_funcptr=True)
+        if python_callable is None:
+            return callback_decorator_wrap                # decorator mode
+        else:
+            return callback_decorator_wrap(python_callable)  # direct mode
+
+    def getctype(self, cdecl, replace_with=''):
+        """Return a string giving the C type 'cdecl', which may be itself
+        a string or a <ctype> object.  If 'replace_with' is given, it gives
+        extra text to append (or insert for more complicated C types), like
+        a variable name, or '*' to get actually the C type 'pointer-to-cdecl'.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        replace_with = replace_with.strip()
+        if (replace_with.startswith('*')
+                and '&[' in self._backend.getcname(cdecl, '&')):
+            replace_with = '(%s)' % replace_with
+        elif replace_with and not replace_with[0] in '[(':
+            replace_with = ' ' + replace_with
+        return self._backend.getcname(cdecl, replace_with)
+
+    def gc(self, cdata, destructor, size=0):
+        """Return a new cdata object that points to the same
+        data.  Later, when this new cdata object is garbage-collected,
+        'destructor(old_cdata_object)' will be called.
+
+        The optional 'size' gives an estimate of the size, used to
+        trigger the garbage collection more eagerly.  So far only used
+        on PyPy.  It tells the GC that the returned object keeps alive
+        roughly 'size' bytes of external memory.
+        """
+        return self._backend.gcp(cdata, destructor, size)
+
+    def _get_cached_btype(self, type):
+        assert self._lock.acquire(False) is False
+        # call me with the lock!
+        try:
+            BType = self._cached_btypes[type]
+        except KeyError:
+            finishlist = []
+            BType = type.get_cached_btype(self, finishlist)
+            for type in finishlist:
+                type.finish_backend_type(self, finishlist)
+        return BType
+
+    def verify(self, source='', tmpdir=None, **kwargs):
+        """Verify that the current ffi signatures compile on this
+        machine, and return a dynamic library object.  The dynamic
+        library can be used to call functions and access global
+        variables declared in this 'ffi'.  The library is compiled
+        by the C compiler: it gives you C-level API compatibility
+        (including calling macros).  This is unlike 'ffi.dlopen()',
+        which requires binary compatibility in the signatures.
+        """
+        from .verifier import Verifier, _caller_dir_pycache
+        #
+        # If set_unicode(True) was called, insert the UNICODE and
+        # _UNICODE macro declarations
+        if self._windows_unicode:
+            self._apply_windows_unicode(kwargs)
+        #
+        # Set the tmpdir here, and not in Verifier.__init__: it picks
+        # up the caller's directory, which we want to be the caller of
+        # ffi.verify(), as opposed to the caller of Veritier().
+        tmpdir = tmpdir or _caller_dir_pycache()
+        #
+        # Make a Verifier() and use it to load the library.
+        self.verifier = Verifier(self, source, tmpdir, **kwargs)
+        lib = self.verifier.load_library()
+        #
+        # Save the loaded library for keep-alive purposes, even
+        # if the caller doesn't keep it alive itself (it should).
+        self._libraries.append(lib)
+        return lib
+
+    def _get_errno(self):
+        return self._backend.get_errno()
+    def _set_errno(self, errno):
+        self._backend.set_errno(errno)
+    errno = property(_get_errno, _set_errno, None,
+                     "the value of 'errno' from/to the C calls")
+
+    def getwinerror(self, code=-1):
+        return self._backend.getwinerror(code)
+
+    def _pointer_to(self, ctype):
+        with self._lock:
+            return model.pointer_cache(self, ctype)
+
+    def addressof(self, cdata, *fields_or_indexes):
+        """Return the address of a <cdata 'struct-or-union'>.
+        If 'fields_or_indexes' are given, returns the address of that
+        field or array item in the structure or array, recursively in
+        case of nested structures.
+        """
+        try:
+            ctype = self._backend.typeof(cdata)
+        except TypeError:
+            if '__addressof__' in type(cdata).__dict__:
+                return type(cdata).__addressof__(cdata, *fields_or_indexes)
+            raise
+        if fields_or_indexes:
+            ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes)
+        else:
+            if ctype.kind == "pointer":
+                raise TypeError("addressof(pointer)")
+            offset = 0
+        ctypeptr = self._pointer_to(ctype)
+        return self._backend.rawaddressof(ctypeptr, cdata, offset)
+
+    def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes):
+        ctype, offset = self._backend.typeoffsetof(ctype, field_or_index)
+        for field1 in fields_or_indexes:
+            ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1)
+            offset += offset1
+        return ctype, offset
+
+    def include(self, ffi_to_include):
+        """Includes the typedefs, structs, unions and enums defined
+        in another FFI instance.  Usage is similar to a #include in C,
+        where a part of the program might include types defined in
+        another part for its own usage.  Note that the include()
+        method has no effect on functions, constants and global
+        variables, which must anyway be accessed directly from the
+        lib object returned by the original FFI instance.
+        """
+        if not isinstance(ffi_to_include, FFI):
+            raise TypeError("ffi.include() expects an argument that is also of"
+                            " type cffi.FFI, not %r" % (
+                                type(ffi_to_include).__name__,))
+        if ffi_to_include is self:
+            raise ValueError("self.include(self)")
+        with ffi_to_include._lock:
+            with self._lock:
+                self._parser.include(ffi_to_include._parser)
+                self._cdefsources.append('[')
+                self._cdefsources.extend(ffi_to_include._cdefsources)
+                self._cdefsources.append(']')
+                self._included_ffis.append(ffi_to_include)
+
+    def new_handle(self, x):
+        return self._backend.newp_handle(self.BVoidP, x)
+
+    def from_handle(self, x):
+        return self._backend.from_handle(x)
+
+    def release(self, x):
+        self._backend.release(x)
+
+    def set_unicode(self, enabled_flag):
+        """Windows: if 'enabled_flag' is True, enable the UNICODE and
+        _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR
+        to be (pointers to) wchar_t.  If 'enabled_flag' is False,
+        declare these types to be (pointers to) plain 8-bit characters.
+        This is mostly for backward compatibility; you usually want True.
+        """
+        if self._windows_unicode is not None:
+            raise ValueError("set_unicode() can only be called once")
+        enabled_flag = bool(enabled_flag)
+        if enabled_flag:
+            self.cdef("typedef wchar_t TBYTE;"
+                      "typedef wchar_t TCHAR;"
+                      "typedef const wchar_t *LPCTSTR;"
+                      "typedef const wchar_t *PCTSTR;"
+                      "typedef wchar_t *LPTSTR;"
+                      "typedef wchar_t *PTSTR;"
+                      "typedef TBYTE *PTBYTE;"
+                      "typedef TCHAR *PTCHAR;")
+        else:
+            self.cdef("typedef char TBYTE;"
+                      "typedef char TCHAR;"
+                      "typedef const char *LPCTSTR;"
+                      "typedef const char *PCTSTR;"
+                      "typedef char *LPTSTR;"
+                      "typedef char *PTSTR;"
+                      "typedef TBYTE *PTBYTE;"
+                      "typedef TCHAR *PTCHAR;")
+        self._windows_unicode = enabled_flag
+
+    def _apply_windows_unicode(self, kwds):
+        defmacros = kwds.get('define_macros', ())
+        if not isinstance(defmacros, (list, tuple)):
+            raise TypeError("'define_macros' must be a list or tuple")
+        defmacros = list(defmacros) + [('UNICODE', '1'),
+                                       ('_UNICODE', '1')]
+        kwds['define_macros'] = defmacros
+
+    def _apply_embedding_fix(self, kwds):
+        # must include an argument like "-lpython2.7" for the compiler
+        def ensure(key, value):
+            lst = kwds.setdefault(key, [])
+            if value not in lst:
+                lst.append(value)
+        #
+        if '__pypy__' in sys.builtin_module_names:
+            import os
+            if sys.platform == "win32":
+                # we need 'libpypy-c.lib'.  Current distributions of
+                # pypy (>= 4.1) contain it as 'libs/python27.lib'.
+                pythonlib = "python{0[0]}{0[1]}".format(sys.version_info)
+                if hasattr(sys, 'prefix'):
+                    ensure('library_dirs', os.path.join(sys.prefix, 'libs'))
+            else:
+                # we need 'libpypy-c.{so,dylib}', which should be by
+                # default located in 'sys.prefix/bin' for installed
+                # systems.
+                if sys.version_info < (3,):
+                    pythonlib = "pypy-c"
+                else:
+                    pythonlib = "pypy3-c"
+                if hasattr(sys, 'prefix'):
+                    ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
+            # On uninstalled pypy's, the libpypy-c is typically found in
+            # .../pypy/goal/.
+            if hasattr(sys, 'prefix'):
+                ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal'))
+        else:
+            if sys.platform == "win32":
+                template = "python%d%d"
+                if hasattr(sys, 'gettotalrefcount'):
+                    template += '_d'
+            else:
+                try:
+                    import sysconfig
+                except ImportError:    # 2.6
+                    from cffi._shimmed_dist_utils import sysconfig
+                template = "python%d.%d"
+                if sysconfig.get_config_var('DEBUG_EXT'):
+                    template += sysconfig.get_config_var('DEBUG_EXT')
+            pythonlib = (template %
+                    (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
+            if hasattr(sys, 'abiflags'):
+                pythonlib += sys.abiflags
+        ensure('libraries', pythonlib)
+        if sys.platform == "win32":
+            ensure('extra_link_args', '/MANIFEST')
+
+    def set_source(self, module_name, source, source_extension='.c', **kwds):
+        import os
+        if hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() cannot be called several times "
+                             "per ffi object")
+        if not isinstance(module_name, basestring):
+            raise TypeError("'module_name' must be a string")
+        if os.sep in module_name or (os.altsep and os.altsep in module_name):
+            raise ValueError("'module_name' must not contain '/': use a dotted "
+                             "name to make a 'package.module' location")
+        self._assigned_source = (str(module_name), source,
+                                 source_extension, kwds)
+
+    def set_source_pkgconfig(self, module_name, pkgconfig_libs, source,
+                             source_extension='.c', **kwds):
+        from . import pkgconfig
+        if not isinstance(pkgconfig_libs, list):
+            raise TypeError("the pkgconfig_libs argument must be a list "
+                            "of package names")
+        kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs)
+        pkgconfig.merge_flags(kwds, kwds2)
+        self.set_source(module_name, source, source_extension, **kwds)
+
+    def distutils_extension(self, tmpdir='build', verbose=True):
+        from cffi._shimmed_dist_utils import mkpath
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            if hasattr(self, 'verifier'):     # fallback, 'tmpdir' ignored
+                return self.verifier.get_extension()
+            raise ValueError("set_source() must be called before"
+                             " distutils_extension()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        if source is None:
+            raise TypeError("distutils_extension() is only for C extension "
+                            "modules, not for dlopen()-style pure Python "
+                            "modules")
+        mkpath(tmpdir)
+        ext, updated = recompile(self, module_name,
+                                 source, tmpdir=tmpdir, extradir=tmpdir,
+                                 source_extension=source_extension,
+                                 call_c_compiler=False, **kwds)
+        if verbose:
+            if updated:
+                sys.stderr.write("regenerated: %r\n" % (ext.sources[0],))
+            else:
+                sys.stderr.write("not modified: %r\n" % (ext.sources[0],))
+        return ext
+
+    def emit_c_code(self, filename):
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() must be called before emit_c_code()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        if source is None:
+            raise TypeError("emit_c_code() is only for C extension modules, "
+                            "not for dlopen()-style pure Python modules")
+        recompile(self, module_name, source,
+                  c_file=filename, call_c_compiler=False,
+                  uses_ffiplatform=False, **kwds)
+
+    def emit_python_code(self, filename):
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() must be called before emit_c_code()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        if source is not None:
+            raise TypeError("emit_python_code() is only for dlopen()-style "
+                            "pure Python modules, not for C extension modules")
+        recompile(self, module_name, source,
+                  c_file=filename, call_c_compiler=False,
+                  uses_ffiplatform=False, **kwds)
+
+    def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
+        """The 'target' argument gives the final file name of the
+        compiled DLL.  Use '*' to force distutils' choice, suitable for
+        regular CPython C API modules.  Use a file name ending in '.*'
+        to ask for the system's default extension for dynamic libraries
+        (.so/.dll/.dylib).
+
+        The default is '*' when building a non-embedded C API extension,
+        and (module_name + '.*') when building an embedded library.
+        """
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() must be called before compile()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        return recompile(self, module_name, source, tmpdir=tmpdir,
+                         target=target, source_extension=source_extension,
+                         compiler_verbose=verbose, debug=debug, **kwds)
+
+    def init_once(self, func, tag):
+        # Read _init_once_cache[tag], which is either (False, lock) if
+        # we're calling the function now in some thread, or (True, result).
+        # Don't call setdefault() in most cases, to avoid allocating and
+        # immediately freeing a lock; but still use setdefaut() to avoid
+        # races.
+        try:
+            x = self._init_once_cache[tag]
+        except KeyError:
+            x = self._init_once_cache.setdefault(tag, (False, allocate_lock()))
+        # Common case: we got (True, result), so we return the result.
+        if x[0]:
+            return x[1]
+        # Else, it's a lock.  Acquire it to serialize the following tests.
+        with x[1]:
+            # Read again from _init_once_cache the current status.
+            x = self._init_once_cache[tag]
+            if x[0]:
+                return x[1]
+            # Call the function and store the result back.
+            result = func()
+            self._init_once_cache[tag] = (True, result)
+        return result
+
+    def embedding_init_code(self, pysource):
+        if self._embedding:
+            raise ValueError("embedding_init_code() can only be called once")
+        # fix 'pysource' before it gets dumped into the C file:
+        # - remove empty lines at the beginning, so it starts at "line 1"
+        # - dedent, if all non-empty lines are indented
+        # - check for SyntaxErrors
+        import re
+        match = re.match(r'\s*\n', pysource)
+        if match:
+            pysource = pysource[match.end():]
+        lines = pysource.splitlines() or ['']
+        prefix = re.match(r'\s*', lines[0]).group()
+        for i in range(1, len(lines)):
+            line = lines[i]
+            if line.rstrip():
+                while not line.startswith(prefix):
+                    prefix = prefix[:-1]
+        i = len(prefix)
+        lines = [line[i:]+'\n' for line in lines]
+        pysource = ''.join(lines)
+        #
+        compile(pysource, "cffi_init", "exec")
+        #
+        self._embedding = pysource
+
+    def def_extern(self, *args, **kwds):
+        raise ValueError("ffi.def_extern() is only available on API-mode FFI "
+                         "objects")
+
+    def list_types(self):
+        """Returns the user type names known to this FFI instance.
+        This returns a tuple containing three lists of names:
+        (typedef_names, names_of_structs, names_of_unions)
+        """
+        typedefs = []
+        structs = []
+        unions = []
+        for key in self._parser._declarations:
+            if key.startswith('typedef '):
+                typedefs.append(key[8:])
+            elif key.startswith('struct '):
+                structs.append(key[7:])
+            elif key.startswith('union '):
+                unions.append(key[6:])
+        typedefs.sort()
+        structs.sort()
+        unions.sort()
+        return (typedefs, structs, unions)
+
+
+def _load_backend_lib(backend, name, flags):
+    import os
+    if not isinstance(name, basestring):
+        if sys.platform != "win32" or name is not None:
+            return backend.load_library(name, flags)
+        name = "c"    # Windows: load_library(None) fails, but this works
+                      # on Python 2 (backward compatibility hack only)
+    first_error = None
+    if '.' in name or '/' in name or os.sep in name:
+        try:
+            return backend.load_library(name, flags)
+        except OSError as e:
+            first_error = e
+    import ctypes.util
+    path = ctypes.util.find_library(name)
+    if path is None:
+        if name == "c" and sys.platform == "win32" and sys.version_info >= (3,):
+            raise OSError("dlopen(None) cannot work on Windows for Python 3 "
+                          "(see http://bugs.python.org/issue23606)")
+        msg = ("ctypes.util.find_library() did not manage "
+               "to locate a library called %r" % (name,))
+        if first_error is not None:
+            msg = "%s.  Additionally, %s" % (first_error, msg)
+        raise OSError(msg)
+    return backend.load_library(path, flags)
+
+def _make_ffi_library(ffi, libname, flags):
+    backend = ffi._backend
+    backendlib = _load_backend_lib(backend, libname, flags)
+    #
+    def accessor_function(name):
+        key = 'function ' + name
+        tp, _ = ffi._parser._declarations[key]
+        BType = ffi._get_cached_btype(tp)
+        value = backendlib.load_function(BType, name)
+        library.__dict__[name] = value
+    #
+    def accessor_variable(name):
+        key = 'variable ' + name
+        tp, _ = ffi._parser._declarations[key]
+        BType = ffi._get_cached_btype(tp)
+        read_variable = backendlib.read_variable
+        write_variable = backendlib.write_variable
+        setattr(FFILibrary, name, property(
+            lambda self: read_variable(BType, name),
+            lambda self, value: write_variable(BType, name, value)))
+    #
+    def addressof_var(name):
+        try:
+            return addr_variables[name]
+        except KeyError:
+            with ffi._lock:
+                if name not in addr_variables:
+                    key = 'variable ' + name
+                    tp, _ = ffi._parser._declarations[key]
+                    BType = ffi._get_cached_btype(tp)
+                    if BType.kind != 'array':
+                        BType = model.pointer_cache(ffi, BType)
+                    p = backendlib.load_function(BType, name)
+                    addr_variables[name] = p
+            return addr_variables[name]
+    #
+    def accessor_constant(name):
+        raise NotImplementedError("non-integer constant '%s' cannot be "
+                                  "accessed from a dlopen() library" % (name,))
+    #
+    def accessor_int_constant(name):
+        library.__dict__[name] = ffi._parser._int_constants[name]
+    #
+    accessors = {}
+    accessors_version = [False]
+    addr_variables = {}
+    #
+    def update_accessors():
+        if accessors_version[0] is ffi._cdef_version:
+            return
+        #
+        for key, (tp, _) in ffi._parser._declarations.items():
+            if not isinstance(tp, model.EnumType):
+                tag, name = key.split(' ', 1)
+                if tag == 'function':
+                    accessors[name] = accessor_function
+                elif tag == 'variable':
+                    accessors[name] = accessor_variable
+                elif tag == 'constant':
+                    accessors[name] = accessor_constant
+            else:
+                for i, enumname in enumerate(tp.enumerators):
+                    def accessor_enum(name, tp=tp, i=i):
+                        tp.check_not_partial()
+                        library.__dict__[name] = tp.enumvalues[i]
+                    accessors[enumname] = accessor_enum
+        for name in ffi._parser._int_constants:
+            accessors.setdefault(name, accessor_int_constant)
+        accessors_version[0] = ffi._cdef_version
+    #
+    def make_accessor(name):
+        with ffi._lock:
+            if name in library.__dict__ or name in FFILibrary.__dict__:
+                return    # added by another thread while waiting for the lock
+            if name not in accessors:
+                update_accessors()
+                if name not in accessors:
+                    raise AttributeError(name)
+            accessors[name](name)
+    #
+    class FFILibrary(object):
+        def __getattr__(self, name):
+            make_accessor(name)
+            return getattr(self, name)
+        def __setattr__(self, name, value):
+            try:
+                property = getattr(self.__class__, name)
+            except AttributeError:
+                make_accessor(name)
+                setattr(self, name, value)
+            else:
+                property.__set__(self, value)
+        def __dir__(self):
+            with ffi._lock:
+                update_accessors()
+                return accessors.keys()
+        def __addressof__(self, name):
+            if name in library.__dict__:
+                return library.__dict__[name]
+            if name in FFILibrary.__dict__:
+                return addressof_var(name)
+            make_accessor(name)
+            if name in library.__dict__:
+                return library.__dict__[name]
+            if name in FFILibrary.__dict__:
+                return addressof_var(name)
+            raise AttributeError("cffi library has no function or "
+                                 "global variable named '%s'" % (name,))
+        def __cffi_close__(self):
+            backendlib.close_lib()
+            self.__dict__.clear()
+    #
+    if isinstance(libname, basestring):
+        try:
+            if not isinstance(libname, str):    # unicode, on Python 2
+                libname = libname.encode('utf-8')
+            FFILibrary.__name__ = 'FFILibrary_%s' % libname
+        except UnicodeError:
+            pass
+    library = FFILibrary()
+    return library, library.__dict__
+
+def _builtin_function_type(func):
+    # a hack to make at least ffi.typeof(builtin_function) work,
+    # if the builtin function was obtained by 'vengine_cpy'.
+    import sys
+    try:
+        module = sys.modules[func.__module__]
+        ffi = module._cffi_original_ffi
+        types_of_builtin_funcs = module._cffi_types_of_builtin_funcs
+        tp = types_of_builtin_funcs[func]
+    except (KeyError, AttributeError, TypeError):
+        return None
+    else:
+        with ffi._lock:
+            return ffi._get_cached_btype(tp)
diff --git a/.venv/lib/python3.8/site-packages/cffi/backend_ctypes.py b/.venv/lib/python3.8/site-packages/cffi/backend_ctypes.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7956a79cfb1c3d28a2ad22a40b261ae7dbbbb5f
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/backend_ctypes.py
@@ -0,0 +1,1121 @@
+import ctypes, ctypes.util, operator, sys
+from . import model
+
+if sys.version_info < (3,):
+    bytechr = chr
+else:
+    unicode = str
+    long = int
+    xrange = range
+    bytechr = lambda num: bytes([num])
+
+class CTypesType(type):
+    pass
+
+class CTypesData(object):
+    __metaclass__ = CTypesType
+    __slots__ = ['__weakref__']
+    __name__ = '<cdata>'
+
+    def __init__(self, *args):
+        raise TypeError("cannot instantiate %r" % (self.__class__,))
+
+    @classmethod
+    def _newp(cls, init):
+        raise TypeError("expected a pointer or array ctype, got '%s'"
+                        % (cls._get_c_name(),))
+
+    @staticmethod
+    def _to_ctypes(value):
+        raise TypeError
+
+    @classmethod
+    def _arg_to_ctypes(cls, *value):
+        try:
+            ctype = cls._ctype
+        except AttributeError:
+            raise TypeError("cannot create an instance of %r" % (cls,))
+        if value:
+            res = cls._to_ctypes(*value)
+            if not isinstance(res, ctype):
+                res = cls._ctype(res)
+        else:
+            res = cls._ctype()
+        return res
+
+    @classmethod
+    def _create_ctype_obj(cls, init):
+        if init is None:
+            return cls._arg_to_ctypes()
+        else:
+            return cls._arg_to_ctypes(init)
+
+    @staticmethod
+    def _from_ctypes(ctypes_value):
+        raise TypeError
+
+    @classmethod
+    def _get_c_name(cls, replace_with=''):
+        return cls._reftypename.replace(' &', replace_with)
+
+    @classmethod
+    def _fix_class(cls):
+        cls.__name__ = 'CData<%s>' % (cls._get_c_name(),)
+        cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),)
+        cls.__module__ = 'ffi'
+
+    def _get_own_repr(self):
+        raise NotImplementedError
+
+    def _addr_repr(self, address):
+        if address == 0:
+            return 'NULL'
+        else:
+            if address < 0:
+                address += 1 << (8*ctypes.sizeof(ctypes.c_void_p))
+            return '0x%x' % address
+
+    def __repr__(self, c_name=None):
+        own = self._get_own_repr()
+        return '<cdata %r %s>' % (c_name or self._get_c_name(), own)
+
+    def _convert_to_address(self, BClass):
+        if BClass is None:
+            raise TypeError("cannot convert %r to an address" % (
+                self._get_c_name(),))
+        else:
+            raise TypeError("cannot convert %r to %r" % (
+                self._get_c_name(), BClass._get_c_name()))
+
+    @classmethod
+    def _get_size(cls):
+        return ctypes.sizeof(cls._ctype)
+
+    def _get_size_of_instance(self):
+        return ctypes.sizeof(self._ctype)
+
+    @classmethod
+    def _cast_from(cls, source):
+        raise TypeError("cannot cast to %r" % (cls._get_c_name(),))
+
+    def _cast_to_integer(self):
+        return self._convert_to_address(None)
+
+    @classmethod
+    def _alignment(cls):
+        return ctypes.alignment(cls._ctype)
+
+    def __iter__(self):
+        raise TypeError("cdata %r does not support iteration" % (
+            self._get_c_name()),)
+
+    def _make_cmp(name):
+        cmpfunc = getattr(operator, name)
+        def cmp(self, other):
+            v_is_ptr = not isinstance(self, CTypesGenericPrimitive)
+            w_is_ptr = (isinstance(other, CTypesData) and
+                           not isinstance(other, CTypesGenericPrimitive))
+            if v_is_ptr and w_is_ptr:
+                return cmpfunc(self._convert_to_address(None),
+                               other._convert_to_address(None))
+            elif v_is_ptr or w_is_ptr:
+                return NotImplemented
+            else:
+                if isinstance(self, CTypesGenericPrimitive):
+                    self = self._value
+                if isinstance(other, CTypesGenericPrimitive):
+                    other = other._value
+                return cmpfunc(self, other)
+        cmp.func_name = name
+        return cmp
+
+    __eq__ = _make_cmp('__eq__')
+    __ne__ = _make_cmp('__ne__')
+    __lt__ = _make_cmp('__lt__')
+    __le__ = _make_cmp('__le__')
+    __gt__ = _make_cmp('__gt__')
+    __ge__ = _make_cmp('__ge__')
+
+    def __hash__(self):
+        return hash(self._convert_to_address(None))
+
+    def _to_string(self, maxlen):
+        raise TypeError("string(): %r" % (self,))
+
+
+class CTypesGenericPrimitive(CTypesData):
+    __slots__ = []
+
+    def __hash__(self):
+        return hash(self._value)
+
+    def _get_own_repr(self):
+        return repr(self._from_ctypes(self._value))
+
+
+class CTypesGenericArray(CTypesData):
+    __slots__ = []
+
+    @classmethod
+    def _newp(cls, init):
+        return cls(init)
+
+    def __iter__(self):
+        for i in xrange(len(self)):
+            yield self[i]
+
+    def _get_own_repr(self):
+        return self._addr_repr(ctypes.addressof(self._blob))
+
+
+class CTypesGenericPtr(CTypesData):
+    __slots__ = ['_address', '_as_ctype_ptr']
+    _automatic_casts = False
+    kind = "pointer"
+
+    @classmethod
+    def _newp(cls, init):
+        return cls(init)
+
+    @classmethod
+    def _cast_from(cls, source):
+        if source is None:
+            address = 0
+        elif isinstance(source, CTypesData):
+            address = source._cast_to_integer()
+        elif isinstance(source, (int, long)):
+            address = source
+        else:
+            raise TypeError("bad type for cast to %r: %r" %
+                            (cls, type(source).__name__))
+        return cls._new_pointer_at(address)
+
+    @classmethod
+    def _new_pointer_at(cls, address):
+        self = cls.__new__(cls)
+        self._address = address
+        self._as_ctype_ptr = ctypes.cast(address, cls._ctype)
+        return self
+
+    def _get_own_repr(self):
+        try:
+            return self._addr_repr(self._address)
+        except AttributeError:
+            return '???'
+
+    def _cast_to_integer(self):
+        return self._address
+
+    def __nonzero__(self):
+        return bool(self._address)
+    __bool__ = __nonzero__
+
+    @classmethod
+    def _to_ctypes(cls, value):
+        if not isinstance(value, CTypesData):
+            raise TypeError("unexpected %s object" % type(value).__name__)
+        address = value._convert_to_address(cls)
+        return ctypes.cast(address, cls._ctype)
+
+    @classmethod
+    def _from_ctypes(cls, ctypes_ptr):
+        address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0
+        return cls._new_pointer_at(address)
+
+    @classmethod
+    def _initialize(cls, ctypes_ptr, value):
+        if value:
+            ctypes_ptr.contents = cls._to_ctypes(value).contents
+
+    def _convert_to_address(self, BClass):
+        if (BClass in (self.__class__, None) or BClass._automatic_casts
+            or self._automatic_casts):
+            return self._address
+        else:
+            return CTypesData._convert_to_address(self, BClass)
+
+
+class CTypesBaseStructOrUnion(CTypesData):
+    __slots__ = ['_blob']
+
+    @classmethod
+    def _create_ctype_obj(cls, init):
+        # may be overridden
+        raise TypeError("cannot instantiate opaque type %s" % (cls,))
+
+    def _get_own_repr(self):
+        return self._addr_repr(ctypes.addressof(self._blob))
+
+    @classmethod
+    def _offsetof(cls, fieldname):
+        return getattr(cls._ctype, fieldname).offset
+
+    def _convert_to_address(self, BClass):
+        if getattr(BClass, '_BItem', None) is self.__class__:
+            return ctypes.addressof(self._blob)
+        else:
+            return CTypesData._convert_to_address(self, BClass)
+
+    @classmethod
+    def _from_ctypes(cls, ctypes_struct_or_union):
+        self = cls.__new__(cls)
+        self._blob = ctypes_struct_or_union
+        return self
+
+    @classmethod
+    def _to_ctypes(cls, value):
+        return value._blob
+
+    def __repr__(self, c_name=None):
+        return CTypesData.__repr__(self, c_name or self._get_c_name(' &'))
+
+
+class CTypesBackend(object):
+
+    PRIMITIVE_TYPES = {
+        'char': ctypes.c_char,
+        'short': ctypes.c_short,
+        'int': ctypes.c_int,
+        'long': ctypes.c_long,
+        'long long': ctypes.c_longlong,
+        'signed char': ctypes.c_byte,
+        'unsigned char': ctypes.c_ubyte,
+        'unsigned short': ctypes.c_ushort,
+        'unsigned int': ctypes.c_uint,
+        'unsigned long': ctypes.c_ulong,
+        'unsigned long long': ctypes.c_ulonglong,
+        'float': ctypes.c_float,
+        'double': ctypes.c_double,
+        '_Bool': ctypes.c_bool,
+        }
+
+    for _name in ['unsigned long long', 'unsigned long',
+                  'unsigned int', 'unsigned short', 'unsigned char']:
+        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
+        PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_void_p):
+            PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_size_t):
+            PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name]
+
+    for _name in ['long long', 'long', 'int', 'short', 'signed char']:
+        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
+        PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_void_p):
+            PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name]
+            PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_size_t):
+            PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name]
+
+
+    def __init__(self):
+        self.RTLD_LAZY = 0   # not supported anyway by ctypes
+        self.RTLD_NOW  = 0
+        self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL
+        self.RTLD_LOCAL = ctypes.RTLD_LOCAL
+
+    def set_ffi(self, ffi):
+        self.ffi = ffi
+
+    def _get_types(self):
+        return CTypesData, CTypesType
+
+    def load_library(self, path, flags=0):
+        cdll = ctypes.CDLL(path, flags)
+        return CTypesLibrary(self, cdll)
+
+    def new_void_type(self):
+        class CTypesVoid(CTypesData):
+            __slots__ = []
+            _reftypename = 'void &'
+            @staticmethod
+            def _from_ctypes(novalue):
+                return None
+            @staticmethod
+            def _to_ctypes(novalue):
+                if novalue is not None:
+                    raise TypeError("None expected, got %s object" %
+                                    (type(novalue).__name__,))
+                return None
+        CTypesVoid._fix_class()
+        return CTypesVoid
+
+    def new_primitive_type(self, name):
+        if name == 'wchar_t':
+            raise NotImplementedError(name)
+        ctype = self.PRIMITIVE_TYPES[name]
+        if name == 'char':
+            kind = 'char'
+        elif name in ('float', 'double'):
+            kind = 'float'
+        else:
+            if name in ('signed char', 'unsigned char'):
+                kind = 'byte'
+            elif name == '_Bool':
+                kind = 'bool'
+            else:
+                kind = 'int'
+            is_signed = (ctype(-1).value == -1)
+        #
+        def _cast_source_to_int(source):
+            if isinstance(source, (int, long, float)):
+                source = int(source)
+            elif isinstance(source, CTypesData):
+                source = source._cast_to_integer()
+            elif isinstance(source, bytes):
+                source = ord(source)
+            elif source is None:
+                source = 0
+            else:
+                raise TypeError("bad type for cast to %r: %r" %
+                                (CTypesPrimitive, type(source).__name__))
+            return source
+        #
+        kind1 = kind
+        class CTypesPrimitive(CTypesGenericPrimitive):
+            __slots__ = ['_value']
+            _ctype = ctype
+            _reftypename = '%s &' % name
+            kind = kind1
+
+            def __init__(self, value):
+                self._value = value
+
+            @staticmethod
+            def _create_ctype_obj(init):
+                if init is None:
+                    return ctype()
+                return ctype(CTypesPrimitive._to_ctypes(init))
+
+            if kind == 'int' or kind == 'byte':
+                @classmethod
+                def _cast_from(cls, source):
+                    source = _cast_source_to_int(source)
+                    source = ctype(source).value     # cast within range
+                    return cls(source)
+                def __int__(self):
+                    return self._value
+
+            if kind == 'bool':
+                @classmethod
+                def _cast_from(cls, source):
+                    if not isinstance(source, (int, long, float)):
+                        source = _cast_source_to_int(source)
+                    return cls(bool(source))
+                def __int__(self):
+                    return int(self._value)
+
+            if kind == 'char':
+                @classmethod
+                def _cast_from(cls, source):
+                    source = _cast_source_to_int(source)
+                    source = bytechr(source & 0xFF)
+                    return cls(source)
+                def __int__(self):
+                    return ord(self._value)
+
+            if kind == 'float':
+                @classmethod
+                def _cast_from(cls, source):
+                    if isinstance(source, float):
+                        pass
+                    elif isinstance(source, CTypesGenericPrimitive):
+                        if hasattr(source, '__float__'):
+                            source = float(source)
+                        else:
+                            source = int(source)
+                    else:
+                        source = _cast_source_to_int(source)
+                    source = ctype(source).value     # fix precision
+                    return cls(source)
+                def __int__(self):
+                    return int(self._value)
+                def __float__(self):
+                    return self._value
+
+            _cast_to_integer = __int__
+
+            if kind == 'int' or kind == 'byte' or kind == 'bool':
+                @staticmethod
+                def _to_ctypes(x):
+                    if not isinstance(x, (int, long)):
+                        if isinstance(x, CTypesData):
+                            x = int(x)
+                        else:
+                            raise TypeError("integer expected, got %s" %
+                                            type(x).__name__)
+                    if ctype(x).value != x:
+                        if not is_signed and x < 0:
+                            raise OverflowError("%s: negative integer" % name)
+                        else:
+                            raise OverflowError("%s: integer out of bounds"
+                                                % name)
+                    return x
+
+            if kind == 'char':
+                @staticmethod
+                def _to_ctypes(x):
+                    if isinstance(x, bytes) and len(x) == 1:
+                        return x
+                    if isinstance(x, CTypesPrimitive):    # <CData <char>>
+                        return x._value
+                    raise TypeError("character expected, got %s" %
+                                    type(x).__name__)
+                def __nonzero__(self):
+                    return ord(self._value) != 0
+            else:
+                def __nonzero__(self):
+                    return self._value != 0
+            __bool__ = __nonzero__
+
+            if kind == 'float':
+                @staticmethod
+                def _to_ctypes(x):
+                    if not isinstance(x, (int, long, float, CTypesData)):
+                        raise TypeError("float expected, got %s" %
+                                        type(x).__name__)
+                    return ctype(x).value
+
+            @staticmethod
+            def _from_ctypes(value):
+                return getattr(value, 'value', value)
+
+            @staticmethod
+            def _initialize(blob, init):
+                blob.value = CTypesPrimitive._to_ctypes(init)
+
+            if kind == 'char':
+                def _to_string(self, maxlen):
+                    return self._value
+            if kind == 'byte':
+                def _to_string(self, maxlen):
+                    return chr(self._value & 0xff)
+        #
+        CTypesPrimitive._fix_class()
+        return CTypesPrimitive
+
+    def new_pointer_type(self, BItem):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
+            kind = 'charp'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'bytep'
+        elif BItem is getbtype(model.void_type):
+            kind = 'voidp'
+        else:
+            kind = 'generic'
+        #
+        class CTypesPtr(CTypesGenericPtr):
+            __slots__ = ['_own']
+            if kind == 'charp':
+                __slots__ += ['__as_strbuf']
+            _BItem = BItem
+            if hasattr(BItem, '_ctype'):
+                _ctype = ctypes.POINTER(BItem._ctype)
+                _bitem_size = ctypes.sizeof(BItem._ctype)
+            else:
+                _ctype = ctypes.c_void_p
+            if issubclass(BItem, CTypesGenericArray):
+                _reftypename = BItem._get_c_name('(* &)')
+            else:
+                _reftypename = BItem._get_c_name(' * &')
+
+            def __init__(self, init):
+                ctypeobj = BItem._create_ctype_obj(init)
+                if kind == 'charp':
+                    self.__as_strbuf = ctypes.create_string_buffer(
+                        ctypeobj.value + b'\x00')
+                    self._as_ctype_ptr = ctypes.cast(
+                        self.__as_strbuf, self._ctype)
+                else:
+                    self._as_ctype_ptr = ctypes.pointer(ctypeobj)
+                self._address = ctypes.cast(self._as_ctype_ptr,
+                                            ctypes.c_void_p).value
+                self._own = True
+
+            def __add__(self, other):
+                if isinstance(other, (int, long)):
+                    return self._new_pointer_at(self._address +
+                                                other * self._bitem_size)
+                else:
+                    return NotImplemented
+
+            def __sub__(self, other):
+                if isinstance(other, (int, long)):
+                    return self._new_pointer_at(self._address -
+                                                other * self._bitem_size)
+                elif type(self) is type(other):
+                    return (self._address - other._address) // self._bitem_size
+                else:
+                    return NotImplemented
+
+            def __getitem__(self, index):
+                if getattr(self, '_own', False) and index != 0:
+                    raise IndexError
+                return BItem._from_ctypes(self._as_ctype_ptr[index])
+
+            def __setitem__(self, index, value):
+                self._as_ctype_ptr[index] = BItem._to_ctypes(value)
+
+            if kind == 'charp' or kind == 'voidp':
+                @classmethod
+                def _arg_to_ctypes(cls, *value):
+                    if value and isinstance(value[0], bytes):
+                        return ctypes.c_char_p(value[0])
+                    else:
+                        return super(CTypesPtr, cls)._arg_to_ctypes(*value)
+
+            if kind == 'charp' or kind == 'bytep':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = sys.maxsize
+                    p = ctypes.cast(self._as_ctype_ptr,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != b'\x00':
+                        n += 1
+                    return b''.join([p[i] for i in range(n)])
+
+            def _get_own_repr(self):
+                if getattr(self, '_own', False):
+                    return 'owning %d bytes' % (
+                        ctypes.sizeof(self._as_ctype_ptr.contents),)
+                return super(CTypesPtr, self)._get_own_repr()
+        #
+        if (BItem is self.ffi._get_cached_btype(model.void_type) or
+            BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))):
+            CTypesPtr._automatic_casts = True
+        #
+        CTypesPtr._fix_class()
+        return CTypesPtr
+
+    def new_array_type(self, CTypesPtr, length):
+        if length is None:
+            brackets = ' &[]'
+        else:
+            brackets = ' &[%d]' % length
+        BItem = CTypesPtr._BItem
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
+            kind = 'char'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'byte'
+        else:
+            kind = 'generic'
+        #
+        class CTypesArray(CTypesGenericArray):
+            __slots__ = ['_blob', '_own']
+            if length is not None:
+                _ctype = BItem._ctype * length
+            else:
+                __slots__.append('_ctype')
+            _reftypename = BItem._get_c_name(brackets)
+            _declared_length = length
+            _CTPtr = CTypesPtr
+
+            def __init__(self, init):
+                if length is None:
+                    if isinstance(init, (int, long)):
+                        len1 = init
+                        init = None
+                    elif kind == 'char' and isinstance(init, bytes):
+                        len1 = len(init) + 1    # extra null
+                    else:
+                        init = tuple(init)
+                        len1 = len(init)
+                    self._ctype = BItem._ctype * len1
+                self._blob = self._ctype()
+                self._own = True
+                if init is not None:
+                    self._initialize(self._blob, init)
+
+            @staticmethod
+            def _initialize(blob, init):
+                if isinstance(init, bytes):
+                    init = [init[i:i+1] for i in range(len(init))]
+                else:
+                    if isinstance(init, CTypesGenericArray):
+                        if (len(init) != len(blob) or
+                            not isinstance(init, CTypesArray)):
+                            raise TypeError("length/type mismatch: %s" % (init,))
+                    init = tuple(init)
+                if len(init) > len(blob):
+                    raise IndexError("too many initializers")
+                addr = ctypes.cast(blob, ctypes.c_void_p).value
+                PTR = ctypes.POINTER(BItem._ctype)
+                itemsize = ctypes.sizeof(BItem._ctype)
+                for i, value in enumerate(init):
+                    p = ctypes.cast(addr + i * itemsize, PTR)
+                    BItem._initialize(p.contents, value)
+
+            def __len__(self):
+                return len(self._blob)
+
+            def __getitem__(self, index):
+                if not (0 <= index < len(self._blob)):
+                    raise IndexError
+                return BItem._from_ctypes(self._blob[index])
+
+            def __setitem__(self, index, value):
+                if not (0 <= index < len(self._blob)):
+                    raise IndexError
+                self._blob[index] = BItem._to_ctypes(value)
+
+            if kind == 'char' or kind == 'byte':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = len(self._blob)
+                    p = ctypes.cast(self._blob,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != b'\x00':
+                        n += 1
+                    return b''.join([p[i] for i in range(n)])
+
+            def _get_own_repr(self):
+                if getattr(self, '_own', False):
+                    return 'owning %d bytes' % (ctypes.sizeof(self._blob),)
+                return super(CTypesArray, self)._get_own_repr()
+
+            def _convert_to_address(self, BClass):
+                if BClass in (CTypesPtr, None) or BClass._automatic_casts:
+                    return ctypes.addressof(self._blob)
+                else:
+                    return CTypesData._convert_to_address(self, BClass)
+
+            @staticmethod
+            def _from_ctypes(ctypes_array):
+                self = CTypesArray.__new__(CTypesArray)
+                self._blob = ctypes_array
+                return self
+
+            @staticmethod
+            def _arg_to_ctypes(value):
+                return CTypesPtr._arg_to_ctypes(value)
+
+            def __add__(self, other):
+                if isinstance(other, (int, long)):
+                    return CTypesPtr._new_pointer_at(
+                        ctypes.addressof(self._blob) +
+                        other * ctypes.sizeof(BItem._ctype))
+                else:
+                    return NotImplemented
+
+            @classmethod
+            def _cast_from(cls, source):
+                raise NotImplementedError("casting to %r" % (
+                    cls._get_c_name(),))
+        #
+        CTypesArray._fix_class()
+        return CTypesArray
+
+    def _new_struct_or_union(self, kind, name, base_ctypes_class):
+        #
+        class struct_or_union(base_ctypes_class):
+            pass
+        struct_or_union.__name__ = '%s_%s' % (kind, name)
+        kind1 = kind
+        #
+        class CTypesStructOrUnion(CTypesBaseStructOrUnion):
+            __slots__ = ['_blob']
+            _ctype = struct_or_union
+            _reftypename = '%s &' % (name,)
+            _kind = kind = kind1
+        #
+        CTypesStructOrUnion._fix_class()
+        return CTypesStructOrUnion
+
+    def new_struct_type(self, name):
+        return self._new_struct_or_union('struct', name, ctypes.Structure)
+
+    def new_union_type(self, name):
+        return self._new_struct_or_union('union', name, ctypes.Union)
+
+    def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp,
+                                 totalsize=-1, totalalignment=-1, sflags=0,
+                                 pack=0):
+        if totalsize >= 0 or totalalignment >= 0:
+            raise NotImplementedError("the ctypes backend of CFFI does not support "
+                                      "structures completed by verify(); please "
+                                      "compile and install the _cffi_backend module.")
+        struct_or_union = CTypesStructOrUnion._ctype
+        fnames = [fname for (fname, BField, bitsize) in fields]
+        btypes = [BField for (fname, BField, bitsize) in fields]
+        bitfields = [bitsize for (fname, BField, bitsize) in fields]
+        #
+        bfield_types = {}
+        cfields = []
+        for (fname, BField, bitsize) in fields:
+            if bitsize < 0:
+                cfields.append((fname, BField._ctype))
+                bfield_types[fname] = BField
+            else:
+                cfields.append((fname, BField._ctype, bitsize))
+                bfield_types[fname] = Ellipsis
+        if sflags & 8:
+            struct_or_union._pack_ = 1
+        elif pack:
+            struct_or_union._pack_ = pack
+        struct_or_union._fields_ = cfields
+        CTypesStructOrUnion._bfield_types = bfield_types
+        #
+        @staticmethod
+        def _create_ctype_obj(init):
+            result = struct_or_union()
+            if init is not None:
+                initialize(result, init)
+            return result
+        CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
+        #
+        def initialize(blob, init):
+            if is_union:
+                if len(init) > 1:
+                    raise ValueError("union initializer: %d items given, but "
+                                    "only one supported (use a dict if needed)"
+                                     % (len(init),))
+            if not isinstance(init, dict):
+                if isinstance(init, (bytes, unicode)):
+                    raise TypeError("union initializer: got a str")
+                init = tuple(init)
+                if len(init) > len(fnames):
+                    raise ValueError("too many values for %s initializer" %
+                                     CTypesStructOrUnion._get_c_name())
+                init = dict(zip(fnames, init))
+            addr = ctypes.addressof(blob)
+            for fname, value in init.items():
+                BField, bitsize = name2fieldtype[fname]
+                assert bitsize < 0, \
+                       "not implemented: initializer with bit fields"
+                offset = CTypesStructOrUnion._offsetof(fname)
+                PTR = ctypes.POINTER(BField._ctype)
+                p = ctypes.cast(addr + offset, PTR)
+                BField._initialize(p.contents, value)
+        is_union = CTypesStructOrUnion._kind == 'union'
+        name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
+        #
+        for fname, BField, bitsize in fields:
+            if fname == '':
+                raise NotImplementedError("nested anonymous structs/unions")
+            if hasattr(CTypesStructOrUnion, fname):
+                raise ValueError("the field name %r conflicts in "
+                                 "the ctypes backend" % fname)
+            if bitsize < 0:
+                def getter(self, fname=fname, BField=BField,
+                           offset=CTypesStructOrUnion._offsetof(fname),
+                           PTR=ctypes.POINTER(BField._ctype)):
+                    addr = ctypes.addressof(self._blob)
+                    p = ctypes.cast(addr + offset, PTR)
+                    return BField._from_ctypes(p.contents)
+                def setter(self, value, fname=fname, BField=BField):
+                    setattr(self._blob, fname, BField._to_ctypes(value))
+                #
+                if issubclass(BField, CTypesGenericArray):
+                    setter = None
+                    if BField._declared_length == 0:
+                        def getter(self, fname=fname, BFieldPtr=BField._CTPtr,
+                                   offset=CTypesStructOrUnion._offsetof(fname),
+                                   PTR=ctypes.POINTER(BField._ctype)):
+                            addr = ctypes.addressof(self._blob)
+                            p = ctypes.cast(addr + offset, PTR)
+                            return BFieldPtr._from_ctypes(p)
+                #
+            else:
+                def getter(self, fname=fname, BField=BField):
+                    return BField._from_ctypes(getattr(self._blob, fname))
+                def setter(self, value, fname=fname, BField=BField):
+                    # xxx obscure workaround
+                    value = BField._to_ctypes(value)
+                    oldvalue = getattr(self._blob, fname)
+                    setattr(self._blob, fname, value)
+                    if value != getattr(self._blob, fname):
+                        setattr(self._blob, fname, oldvalue)
+                        raise OverflowError("value too large for bitfield")
+            setattr(CTypesStructOrUnion, fname, property(getter, setter))
+        #
+        CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp))
+        for fname in fnames:
+            if hasattr(CTypesPtr, fname):
+                raise ValueError("the field name %r conflicts in "
+                                 "the ctypes backend" % fname)
+            def getter(self, fname=fname):
+                return getattr(self[0], fname)
+            def setter(self, value, fname=fname):
+                setattr(self[0], fname, value)
+            setattr(CTypesPtr, fname, property(getter, setter))
+
+    def new_function_type(self, BArgs, BResult, has_varargs):
+        nameargs = [BArg._get_c_name() for BArg in BArgs]
+        if has_varargs:
+            nameargs.append('...')
+        nameargs = ', '.join(nameargs)
+        #
+        class CTypesFunctionPtr(CTypesGenericPtr):
+            __slots__ = ['_own_callback', '_name']
+            _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None),
+                                      *[BArg._ctype for BArg in BArgs],
+                                      use_errno=True)
+            _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,))
+
+            def __init__(self, init, error=None):
+                # create a callback to the Python callable init()
+                import traceback
+                assert not has_varargs, "varargs not supported for callbacks"
+                if getattr(BResult, '_ctype', None) is not None:
+                    error = BResult._from_ctypes(
+                        BResult._create_ctype_obj(error))
+                else:
+                    error = None
+                def callback(*args):
+                    args2 = []
+                    for arg, BArg in zip(args, BArgs):
+                        args2.append(BArg._from_ctypes(arg))
+                    try:
+                        res2 = init(*args2)
+                        res2 = BResult._to_ctypes(res2)
+                    except:
+                        traceback.print_exc()
+                        res2 = error
+                    if issubclass(BResult, CTypesGenericPtr):
+                        if res2:
+                            res2 = ctypes.cast(res2, ctypes.c_void_p).value
+                                # .value: http://bugs.python.org/issue1574593
+                        else:
+                            res2 = None
+                    #print repr(res2)
+                    return res2
+                if issubclass(BResult, CTypesGenericPtr):
+                    # The only pointers callbacks can return are void*s:
+                    # http://bugs.python.org/issue5710
+                    callback_ctype = ctypes.CFUNCTYPE(
+                        ctypes.c_void_p,
+                        *[BArg._ctype for BArg in BArgs],
+                        use_errno=True)
+                else:
+                    callback_ctype = CTypesFunctionPtr._ctype
+                self._as_ctype_ptr = callback_ctype(callback)
+                self._address = ctypes.cast(self._as_ctype_ptr,
+                                            ctypes.c_void_p).value
+                self._own_callback = init
+
+            @staticmethod
+            def _initialize(ctypes_ptr, value):
+                if value:
+                    raise NotImplementedError("ctypes backend: not supported: "
+                                          "initializers for function pointers")
+
+            def __repr__(self):
+                c_name = getattr(self, '_name', None)
+                if c_name:
+                    i = self._reftypename.index('(* &)')
+                    if self._reftypename[i-1] not in ' )*':
+                        c_name = ' ' + c_name
+                    c_name = self._reftypename.replace('(* &)', c_name)
+                return CTypesData.__repr__(self, c_name)
+
+            def _get_own_repr(self):
+                if getattr(self, '_own_callback', None) is not None:
+                    return 'calling %r' % (self._own_callback,)
+                return super(CTypesFunctionPtr, self)._get_own_repr()
+
+            def __call__(self, *args):
+                if has_varargs:
+                    assert len(args) >= len(BArgs)
+                    extraargs = args[len(BArgs):]
+                    args = args[:len(BArgs)]
+                else:
+                    assert len(args) == len(BArgs)
+                ctypes_args = []
+                for arg, BArg in zip(args, BArgs):
+                    ctypes_args.append(BArg._arg_to_ctypes(arg))
+                if has_varargs:
+                    for i, arg in enumerate(extraargs):
+                        if arg is None:
+                            ctypes_args.append(ctypes.c_void_p(0))  # NULL
+                            continue
+                        if not isinstance(arg, CTypesData):
+                            raise TypeError(
+                                "argument %d passed in the variadic part "
+                                "needs to be a cdata object (got %s)" %
+                                (1 + len(BArgs) + i, type(arg).__name__))
+                        ctypes_args.append(arg._arg_to_ctypes(arg))
+                result = self._as_ctype_ptr(*ctypes_args)
+                return BResult._from_ctypes(result)
+        #
+        CTypesFunctionPtr._fix_class()
+        return CTypesFunctionPtr
+
+    def new_enum_type(self, name, enumerators, enumvalues, CTypesInt):
+        assert isinstance(name, str)
+        reverse_mapping = dict(zip(reversed(enumvalues),
+                                   reversed(enumerators)))
+        #
+        class CTypesEnum(CTypesInt):
+            __slots__ = []
+            _reftypename = '%s &' % name
+
+            def _get_own_repr(self):
+                value = self._value
+                try:
+                    return '%d: %s' % (value, reverse_mapping[value])
+                except KeyError:
+                    return str(value)
+
+            def _to_string(self, maxlen):
+                value = self._value
+                try:
+                    return reverse_mapping[value]
+                except KeyError:
+                    return str(value)
+        #
+        CTypesEnum._fix_class()
+        return CTypesEnum
+
+    def get_errno(self):
+        return ctypes.get_errno()
+
+    def set_errno(self, value):
+        ctypes.set_errno(value)
+
+    def string(self, b, maxlen=-1):
+        return b._to_string(maxlen)
+
+    def buffer(self, bptr, size=-1):
+        raise NotImplementedError("buffer() with ctypes backend")
+
+    def sizeof(self, cdata_or_BType):
+        if isinstance(cdata_or_BType, CTypesData):
+            return cdata_or_BType._get_size_of_instance()
+        else:
+            assert issubclass(cdata_or_BType, CTypesData)
+            return cdata_or_BType._get_size()
+
+    def alignof(self, BType):
+        assert issubclass(BType, CTypesData)
+        return BType._alignment()
+
+    def newp(self, BType, source):
+        if not issubclass(BType, CTypesData):
+            raise TypeError
+        return BType._newp(source)
+
+    def cast(self, BType, source):
+        return BType._cast_from(source)
+
+    def callback(self, BType, source, error, onerror):
+        assert onerror is None   # XXX not implemented
+        return BType(source, error)
+
+    _weakref_cache_ref = None
+
+    def gcp(self, cdata, destructor, size=0):
+        if self._weakref_cache_ref is None:
+            import weakref
+            class MyRef(weakref.ref):
+                def __eq__(self, other):
+                    myref = self()
+                    return self is other or (
+                        myref is not None and myref is other())
+                def __ne__(self, other):
+                    return not (self == other)
+                def __hash__(self):
+                    try:
+                        return self._hash
+                    except AttributeError:
+                        self._hash = hash(self())
+                        return self._hash
+            self._weakref_cache_ref = {}, MyRef
+        weak_cache, MyRef = self._weakref_cache_ref
+
+        if destructor is None:
+            try:
+                del weak_cache[MyRef(cdata)]
+            except KeyError:
+                raise TypeError("Can remove destructor only on a object "
+                                "previously returned by ffi.gc()")
+            return None
+
+        def remove(k):
+            cdata, destructor = weak_cache.pop(k, (None, None))
+            if destructor is not None:
+                destructor(cdata)
+
+        new_cdata = self.cast(self.typeof(cdata), cdata)
+        assert new_cdata is not cdata
+        weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor)
+        return new_cdata
+
+    typeof = type
+
+    def getcname(self, BType, replace_with):
+        return BType._get_c_name(replace_with)
+
+    def typeoffsetof(self, BType, fieldname, num=0):
+        if isinstance(fieldname, str):
+            if num == 0 and issubclass(BType, CTypesGenericPtr):
+                BType = BType._BItem
+            if not issubclass(BType, CTypesBaseStructOrUnion):
+                raise TypeError("expected a struct or union ctype")
+            BField = BType._bfield_types[fieldname]
+            if BField is Ellipsis:
+                raise TypeError("not supported for bitfields")
+            return (BField, BType._offsetof(fieldname))
+        elif isinstance(fieldname, (int, long)):
+            if issubclass(BType, CTypesGenericArray):
+                BType = BType._CTPtr
+            if not issubclass(BType, CTypesGenericPtr):
+                raise TypeError("expected an array or ptr ctype")
+            BItem = BType._BItem
+            offset = BItem._get_size() * fieldname
+            if offset > sys.maxsize:
+                raise OverflowError
+            return (BItem, offset)
+        else:
+            raise TypeError(type(fieldname))
+
+    def rawaddressof(self, BTypePtr, cdata, offset=None):
+        if isinstance(cdata, CTypesBaseStructOrUnion):
+            ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata))
+        elif isinstance(cdata, CTypesGenericPtr):
+            if offset is None or not issubclass(type(cdata)._BItem,
+                                                CTypesBaseStructOrUnion):
+                raise TypeError("unexpected cdata type")
+            ptr = type(cdata)._to_ctypes(cdata)
+        elif isinstance(cdata, CTypesGenericArray):
+            ptr = type(cdata)._to_ctypes(cdata)
+        else:
+            raise TypeError("expected a <cdata 'struct-or-union'>")
+        if offset:
+            ptr = ctypes.cast(
+                ctypes.c_void_p(
+                    ctypes.cast(ptr, ctypes.c_void_p).value + offset),
+                type(ptr))
+        return BTypePtr._from_ctypes(ptr)
+
+
+class CTypesLibrary(object):
+
+    def __init__(self, backend, cdll):
+        self.backend = backend
+        self.cdll = cdll
+
+    def load_function(self, BType, name):
+        c_func = getattr(self.cdll, name)
+        funcobj = BType._from_ctypes(c_func)
+        funcobj._name = name
+        return funcobj
+
+    def read_variable(self, BType, name):
+        try:
+            ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+        except AttributeError as e:
+            raise NotImplementedError(e)
+        return BType._from_ctypes(ctypes_obj)
+
+    def write_variable(self, BType, name, value):
+        new_ctypes_obj = BType._to_ctypes(value)
+        ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+        ctypes.memmove(ctypes.addressof(ctypes_obj),
+                       ctypes.addressof(new_ctypes_obj),
+                       ctypes.sizeof(BType._ctype))
diff --git a/.venv/lib/python3.8/site-packages/cffi/cffi_opcode.py b/.venv/lib/python3.8/site-packages/cffi/cffi_opcode.py
new file mode 100644
index 0000000000000000000000000000000000000000..6421df62134ce43a10d72b3b404102681574abf3
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/cffi_opcode.py
@@ -0,0 +1,187 @@
+from .error import VerificationError
+
+class CffiOp(object):
+    def __init__(self, op, arg):
+        self.op = op
+        self.arg = arg
+
+    def as_c_expr(self):
+        if self.op is None:
+            assert isinstance(self.arg, str)
+            return '(_cffi_opcode_t)(%s)' % (self.arg,)
+        classname = CLASS_NAME[self.op]
+        return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg)
+
+    def as_python_bytes(self):
+        if self.op is None and self.arg.isdigit():
+            value = int(self.arg)     # non-negative: '-' not in self.arg
+            if value >= 2**31:
+                raise OverflowError("cannot emit %r: limited to 2**31-1"
+                                    % (self.arg,))
+            return format_four_bytes(value)
+        if isinstance(self.arg, str):
+            raise VerificationError("cannot emit to Python: %r" % (self.arg,))
+        return format_four_bytes((self.arg << 8) | self.op)
+
+    def __str__(self):
+        classname = CLASS_NAME.get(self.op, self.op)
+        return '(%s %s)' % (classname, self.arg)
+
+def format_four_bytes(num):
+    return '\\x%02X\\x%02X\\x%02X\\x%02X' % (
+        (num >> 24) & 0xFF,
+        (num >> 16) & 0xFF,
+        (num >>  8) & 0xFF,
+        (num      ) & 0xFF)
+
+OP_PRIMITIVE       = 1
+OP_POINTER         = 3
+OP_ARRAY           = 5
+OP_OPEN_ARRAY      = 7
+OP_STRUCT_UNION    = 9
+OP_ENUM            = 11
+OP_FUNCTION        = 13
+OP_FUNCTION_END    = 15
+OP_NOOP            = 17
+OP_BITFIELD        = 19
+OP_TYPENAME        = 21
+OP_CPYTHON_BLTN_V  = 23   # varargs
+OP_CPYTHON_BLTN_N  = 25   # noargs
+OP_CPYTHON_BLTN_O  = 27   # O  (i.e. a single arg)
+OP_CONSTANT        = 29
+OP_CONSTANT_INT    = 31
+OP_GLOBAL_VAR      = 33
+OP_DLOPEN_FUNC     = 35
+OP_DLOPEN_CONST    = 37
+OP_GLOBAL_VAR_F    = 39
+OP_EXTERN_PYTHON   = 41
+
+PRIM_VOID          = 0
+PRIM_BOOL          = 1
+PRIM_CHAR          = 2
+PRIM_SCHAR         = 3
+PRIM_UCHAR         = 4
+PRIM_SHORT         = 5
+PRIM_USHORT        = 6
+PRIM_INT           = 7
+PRIM_UINT          = 8
+PRIM_LONG          = 9
+PRIM_ULONG         = 10
+PRIM_LONGLONG      = 11
+PRIM_ULONGLONG     = 12
+PRIM_FLOAT         = 13
+PRIM_DOUBLE        = 14
+PRIM_LONGDOUBLE    = 15
+
+PRIM_WCHAR         = 16
+PRIM_INT8          = 17
+PRIM_UINT8         = 18
+PRIM_INT16         = 19
+PRIM_UINT16        = 20
+PRIM_INT32         = 21
+PRIM_UINT32        = 22
+PRIM_INT64         = 23
+PRIM_UINT64        = 24
+PRIM_INTPTR        = 25
+PRIM_UINTPTR       = 26
+PRIM_PTRDIFF       = 27
+PRIM_SIZE          = 28
+PRIM_SSIZE         = 29
+PRIM_INT_LEAST8    = 30
+PRIM_UINT_LEAST8   = 31
+PRIM_INT_LEAST16   = 32
+PRIM_UINT_LEAST16  = 33
+PRIM_INT_LEAST32   = 34
+PRIM_UINT_LEAST32  = 35
+PRIM_INT_LEAST64   = 36
+PRIM_UINT_LEAST64  = 37
+PRIM_INT_FAST8     = 38
+PRIM_UINT_FAST8    = 39
+PRIM_INT_FAST16    = 40
+PRIM_UINT_FAST16   = 41
+PRIM_INT_FAST32    = 42
+PRIM_UINT_FAST32   = 43
+PRIM_INT_FAST64    = 44
+PRIM_UINT_FAST64   = 45
+PRIM_INTMAX        = 46
+PRIM_UINTMAX       = 47
+PRIM_FLOATCOMPLEX  = 48
+PRIM_DOUBLECOMPLEX = 49
+PRIM_CHAR16        = 50
+PRIM_CHAR32        = 51
+
+_NUM_PRIM          = 52
+_UNKNOWN_PRIM          = -1
+_UNKNOWN_FLOAT_PRIM    = -2
+_UNKNOWN_LONG_DOUBLE   = -3
+
+_IO_FILE_STRUCT        = -1
+
+PRIMITIVE_TO_INDEX = {
+    'char':               PRIM_CHAR,
+    'short':              PRIM_SHORT,
+    'int':                PRIM_INT,
+    'long':               PRIM_LONG,
+    'long long':          PRIM_LONGLONG,
+    'signed char':        PRIM_SCHAR,
+    'unsigned char':      PRIM_UCHAR,
+    'unsigned short':     PRIM_USHORT,
+    'unsigned int':       PRIM_UINT,
+    'unsigned long':      PRIM_ULONG,
+    'unsigned long long': PRIM_ULONGLONG,
+    'float':              PRIM_FLOAT,
+    'double':             PRIM_DOUBLE,
+    'long double':        PRIM_LONGDOUBLE,
+    '_cffi_float_complex_t': PRIM_FLOATCOMPLEX,
+    '_cffi_double_complex_t': PRIM_DOUBLECOMPLEX,
+    '_Bool':              PRIM_BOOL,
+    'wchar_t':            PRIM_WCHAR,
+    'char16_t':           PRIM_CHAR16,
+    'char32_t':           PRIM_CHAR32,
+    'int8_t':             PRIM_INT8,
+    'uint8_t':            PRIM_UINT8,
+    'int16_t':            PRIM_INT16,
+    'uint16_t':           PRIM_UINT16,
+    'int32_t':            PRIM_INT32,
+    'uint32_t':           PRIM_UINT32,
+    'int64_t':            PRIM_INT64,
+    'uint64_t':           PRIM_UINT64,
+    'intptr_t':           PRIM_INTPTR,
+    'uintptr_t':          PRIM_UINTPTR,
+    'ptrdiff_t':          PRIM_PTRDIFF,
+    'size_t':             PRIM_SIZE,
+    'ssize_t':            PRIM_SSIZE,
+    'int_least8_t':       PRIM_INT_LEAST8,
+    'uint_least8_t':      PRIM_UINT_LEAST8,
+    'int_least16_t':      PRIM_INT_LEAST16,
+    'uint_least16_t':     PRIM_UINT_LEAST16,
+    'int_least32_t':      PRIM_INT_LEAST32,
+    'uint_least32_t':     PRIM_UINT_LEAST32,
+    'int_least64_t':      PRIM_INT_LEAST64,
+    'uint_least64_t':     PRIM_UINT_LEAST64,
+    'int_fast8_t':        PRIM_INT_FAST8,
+    'uint_fast8_t':       PRIM_UINT_FAST8,
+    'int_fast16_t':       PRIM_INT_FAST16,
+    'uint_fast16_t':      PRIM_UINT_FAST16,
+    'int_fast32_t':       PRIM_INT_FAST32,
+    'uint_fast32_t':      PRIM_UINT_FAST32,
+    'int_fast64_t':       PRIM_INT_FAST64,
+    'uint_fast64_t':      PRIM_UINT_FAST64,
+    'intmax_t':           PRIM_INTMAX,
+    'uintmax_t':          PRIM_UINTMAX,
+    }
+
+F_UNION         = 0x01
+F_CHECK_FIELDS  = 0x02
+F_PACKED        = 0x04
+F_EXTERNAL      = 0x08
+F_OPAQUE        = 0x10
+
+G_FLAGS = dict([('_CFFI_' + _key, globals()[_key])
+                for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED',
+                             'F_EXTERNAL', 'F_OPAQUE']])
+
+CLASS_NAME = {}
+for _name, _value in list(globals().items()):
+    if _name.startswith('OP_') and isinstance(_value, int):
+        CLASS_NAME[_value] = _name[3:]
diff --git a/.venv/lib/python3.8/site-packages/cffi/commontypes.py b/.venv/lib/python3.8/site-packages/cffi/commontypes.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4dae3517009fc3f7ccaf01d97d10df098700d06
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/commontypes.py
@@ -0,0 +1,82 @@
+import sys
+from . import model
+from .error import FFIError
+
+
+COMMON_TYPES = {}
+
+try:
+    # fetch "bool" and all simple Windows types
+    from _cffi_backend import _get_common_types
+    _get_common_types(COMMON_TYPES)
+except ImportError:
+    pass
+
+COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE')
+COMMON_TYPES['bool'] = '_Bool'    # in case we got ImportError above
+COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t'
+COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t'
+
+for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+    if _type.endswith('_t'):
+        COMMON_TYPES[_type] = _type
+del _type
+
+_CACHE = {}
+
+def resolve_common_type(parser, commontype):
+    try:
+        return _CACHE[commontype]
+    except KeyError:
+        cdecl = COMMON_TYPES.get(commontype, commontype)
+        if not isinstance(cdecl, str):
+            result, quals = cdecl, 0    # cdecl is already a BaseType
+        elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+            result, quals = model.PrimitiveType(cdecl), 0
+        elif cdecl == 'set-unicode-needed':
+            raise FFIError("The Windows type %r is only available after "
+                           "you call ffi.set_unicode()" % (commontype,))
+        else:
+            if commontype == cdecl:
+                raise FFIError(
+                    "Unsupported type: %r.  Please look at "
+        "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations "
+                    "and file an issue if you think this type should really "
+                    "be supported." % (commontype,))
+            result, quals = parser.parse_type_and_quals(cdecl)   # recursive
+
+        assert isinstance(result, model.BaseTypeByIdentity)
+        _CACHE[commontype] = result, quals
+        return result, quals
+
+
+# ____________________________________________________________
+# extra types for Windows (most of them are in commontypes.c)
+
+
+def win_common_types():
+    return {
+        "UNICODE_STRING": model.StructType(
+            "_UNICODE_STRING",
+            ["Length",
+             "MaximumLength",
+             "Buffer"],
+            [model.PrimitiveType("unsigned short"),
+             model.PrimitiveType("unsigned short"),
+             model.PointerType(model.PrimitiveType("wchar_t"))],
+            [-1, -1, -1]),
+        "PUNICODE_STRING": "UNICODE_STRING *",
+        "PCUNICODE_STRING": "const UNICODE_STRING *",
+
+        "TBYTE": "set-unicode-needed",
+        "TCHAR": "set-unicode-needed",
+        "LPCTSTR": "set-unicode-needed",
+        "PCTSTR": "set-unicode-needed",
+        "LPTSTR": "set-unicode-needed",
+        "PTSTR": "set-unicode-needed",
+        "PTBYTE": "set-unicode-needed",
+        "PTCHAR": "set-unicode-needed",
+        }
+
+if sys.platform == 'win32':
+    COMMON_TYPES.update(win_common_types())
diff --git a/.venv/lib/python3.8/site-packages/cffi/cparser.py b/.venv/lib/python3.8/site-packages/cffi/cparser.py
new file mode 100644
index 0000000000000000000000000000000000000000..eee83caffbd387ad0fda3868e87c6fb394cc54ee
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/cparser.py
@@ -0,0 +1,1015 @@
+from . import model
+from .commontypes import COMMON_TYPES, resolve_common_type
+from .error import FFIError, CDefError
+try:
+    from . import _pycparser as pycparser
+except ImportError:
+    import pycparser
+import weakref, re, sys
+
+try:
+    if sys.version_info < (3,):
+        import thread as _thread
+    else:
+        import _thread
+    lock = _thread.allocate_lock()
+except ImportError:
+    lock = None
+
+def _workaround_for_static_import_finders():
+    # Issue #392: packaging tools like cx_Freeze can not find these
+    # because pycparser uses exec dynamic import.  This is an obscure
+    # workaround.  This function is never called.
+    import pycparser.yacctab
+    import pycparser.lextab
+
+CDEF_SOURCE_STRING = "<cdef source string>"
+_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$",
+                        re.DOTALL | re.MULTILINE)
+_r_define  = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)"
+                        r"\b((?:[^\n\\]|\\.)*?)$",
+                        re.DOTALL | re.MULTILINE)
+_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE)
+_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}")
+_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
+_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
+_r_words = re.compile(r"\w+|\S")
+_parser_cache = None
+_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
+_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
+_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
+_r_cdecl = re.compile(r"\b__cdecl\b")
+_r_extern_python = re.compile(r'\bextern\s*"'
+                              r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.')
+_r_star_const_space = re.compile(       # matches "* const "
+    r"[*]\s*((const|volatile|restrict)\b\s*)+")
+_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+"
+                              r"\.\.\.")
+_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.")
+
+def _get_parser():
+    global _parser_cache
+    if _parser_cache is None:
+        _parser_cache = pycparser.CParser()
+    return _parser_cache
+
+def _workaround_for_old_pycparser(csource):
+    # Workaround for a pycparser issue (fixed between pycparser 2.10 and
+    # 2.14): "char*const***" gives us a wrong syntax tree, the same as
+    # for "char***(*const)".  This means we can't tell the difference
+    # afterwards.  But "char(*const(***))" gives us the right syntax
+    # tree.  The issue only occurs if there are several stars in
+    # sequence with no parenthesis inbetween, just possibly qualifiers.
+    # Attempt to fix it by adding some parentheses in the source: each
+    # time we see "* const" or "* const *", we add an opening
+    # parenthesis before each star---the hard part is figuring out where
+    # to close them.
+    parts = []
+    while True:
+        match = _r_star_const_space.search(csource)
+        if not match:
+            break
+        #print repr(''.join(parts)+csource), '=>',
+        parts.append(csource[:match.start()])
+        parts.append('('); closing = ')'
+        parts.append(match.group())   # e.g. "* const "
+        endpos = match.end()
+        if csource.startswith('*', endpos):
+            parts.append('('); closing += ')'
+        level = 0
+        i = endpos
+        while i < len(csource):
+            c = csource[i]
+            if c == '(':
+                level += 1
+            elif c == ')':
+                if level == 0:
+                    break
+                level -= 1
+            elif c in ',;=':
+                if level == 0:
+                    break
+            i += 1
+        csource = csource[endpos:i] + closing + csource[i:]
+        #print repr(''.join(parts)+csource)
+    parts.append(csource)
+    return ''.join(parts)
+
+def _preprocess_extern_python(csource):
+    # input: `extern "Python" int foo(int);` or
+    #        `extern "Python" { int foo(int); }`
+    # output:
+    #     void __cffi_extern_python_start;
+    #     int foo(int);
+    #     void __cffi_extern_python_stop;
+    #
+    # input: `extern "Python+C" int foo(int);`
+    # output:
+    #     void __cffi_extern_python_plus_c_start;
+    #     int foo(int);
+    #     void __cffi_extern_python_stop;
+    parts = []
+    while True:
+        match = _r_extern_python.search(csource)
+        if not match:
+            break
+        endpos = match.end() - 1
+        #print
+        #print ''.join(parts)+csource
+        #print '=>'
+        parts.append(csource[:match.start()])
+        if 'C' in match.group(1):
+            parts.append('void __cffi_extern_python_plus_c_start; ')
+        else:
+            parts.append('void __cffi_extern_python_start; ')
+        if csource[endpos] == '{':
+            # grouping variant
+            closing = csource.find('}', endpos)
+            if closing < 0:
+                raise CDefError("'extern \"Python\" {': no '}' found")
+            if csource.find('{', endpos + 1, closing) >= 0:
+                raise NotImplementedError("cannot use { } inside a block "
+                                          "'extern \"Python\" { ... }'")
+            parts.append(csource[endpos+1:closing])
+            csource = csource[closing+1:]
+        else:
+            # non-grouping variant
+            semicolon = csource.find(';', endpos)
+            if semicolon < 0:
+                raise CDefError("'extern \"Python\": no ';' found")
+            parts.append(csource[endpos:semicolon+1])
+            csource = csource[semicolon+1:]
+        parts.append(' void __cffi_extern_python_stop;')
+        #print ''.join(parts)+csource
+        #print
+    parts.append(csource)
+    return ''.join(parts)
+
+def _warn_for_string_literal(csource):
+    if '"' not in csource:
+        return
+    for line in csource.splitlines():
+        if '"' in line and not line.lstrip().startswith('#'):
+            import warnings
+            warnings.warn("String literal found in cdef() or type source. "
+                          "String literals are ignored here, but you should "
+                          "remove them anyway because some character sequences "
+                          "confuse pre-parsing.")
+            break
+
+def _warn_for_non_extern_non_static_global_variable(decl):
+    if not decl.storage:
+        import warnings
+        warnings.warn("Global variable '%s' in cdef(): for consistency "
+                      "with C it should have a storage class specifier "
+                      "(usually 'extern')" % (decl.name,))
+
+def _remove_line_directives(csource):
+    # _r_line_directive matches whole lines, without the final \n, if they
+    # start with '#line' with some spacing allowed, or '#NUMBER'.  This
+    # function stores them away and replaces them with exactly the string
+    # '#line@N', where N is the index in the list 'line_directives'.
+    line_directives = []
+    def replace(m):
+        i = len(line_directives)
+        line_directives.append(m.group())
+        return '#line@%d' % i
+    csource = _r_line_directive.sub(replace, csource)
+    return csource, line_directives
+
+def _put_back_line_directives(csource, line_directives):
+    def replace(m):
+        s = m.group()
+        if not s.startswith('#line@'):
+            raise AssertionError("unexpected #line directive "
+                                 "(should have been processed and removed")
+        return line_directives[int(s[6:])]
+    return _r_line_directive.sub(replace, csource)
+
+def _preprocess(csource):
+    # First, remove the lines of the form '#line N "filename"' because
+    # the "filename" part could confuse the rest
+    csource, line_directives = _remove_line_directives(csource)
+    # Remove comments.  NOTE: this only work because the cdef() section
+    # should not contain any string literals (except in line directives)!
+    def replace_keeping_newlines(m):
+        return ' ' + m.group().count('\n') * '\n'
+    csource = _r_comment.sub(replace_keeping_newlines, csource)
+    # Remove the "#define FOO x" lines
+    macros = {}
+    for match in _r_define.finditer(csource):
+        macroname, macrovalue = match.groups()
+        macrovalue = macrovalue.replace('\\\n', '').strip()
+        macros[macroname] = macrovalue
+    csource = _r_define.sub('', csource)
+    #
+    if pycparser.__version__ < '2.14':
+        csource = _workaround_for_old_pycparser(csource)
+    #
+    # BIG HACK: replace WINAPI or __stdcall with "volatile const".
+    # It doesn't make sense for the return type of a function to be
+    # "volatile volatile const", so we abuse it to detect __stdcall...
+    # Hack number 2 is that "int(volatile *fptr)();" is not valid C
+    # syntax, so we place the "volatile" before the opening parenthesis.
+    csource = _r_stdcall2.sub(' volatile volatile const(', csource)
+    csource = _r_stdcall1.sub(' volatile volatile const ', csource)
+    csource = _r_cdecl.sub(' ', csource)
+    #
+    # Replace `extern "Python"` with start/end markers
+    csource = _preprocess_extern_python(csource)
+    #
+    # Now there should not be any string literal left; warn if we get one
+    _warn_for_string_literal(csource)
+    #
+    # Replace "[...]" with "[__dotdotdotarray__]"
+    csource = _r_partial_array.sub('[__dotdotdotarray__]', csource)
+    #
+    # Replace "...}" with "__dotdotdotNUM__}".  This construction should
+    # occur only at the end of enums; at the end of structs we have "...;}"
+    # and at the end of vararg functions "...);".  Also replace "=...[,}]"
+    # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when
+    # giving an unknown value.
+    matches = list(_r_partial_enum.finditer(csource))
+    for number, match in enumerate(reversed(matches)):
+        p = match.start()
+        if csource[p] == '=':
+            p2 = csource.find('...', p, match.end())
+            assert p2 > p
+            csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number,
+                                                 csource[p2+3:])
+        else:
+            assert csource[p:p+3] == '...'
+            csource = '%s __dotdotdot%d__ %s' % (csource[:p], number,
+                                                 csource[p+3:])
+    # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__"
+    csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource)
+    # Replace "float ..." or "double..." with "__dotdotdotfloat__"
+    csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource)
+    # Replace all remaining "..." with the same name, "__dotdotdot__",
+    # which is declared with a typedef for the purpose of C parsing.
+    csource = csource.replace('...', ' __dotdotdot__ ')
+    # Finally, put back the line directives
+    csource = _put_back_line_directives(csource, line_directives)
+    return csource, macros
+
+def _common_type_names(csource):
+    # Look in the source for what looks like usages of types from the
+    # list of common types.  A "usage" is approximated here as the
+    # appearance of the word, minus a "definition" of the type, which
+    # is the last word in a "typedef" statement.  Approximative only
+    # but should be fine for all the common types.
+    look_for_words = set(COMMON_TYPES)
+    look_for_words.add(';')
+    look_for_words.add(',')
+    look_for_words.add('(')
+    look_for_words.add(')')
+    look_for_words.add('typedef')
+    words_used = set()
+    is_typedef = False
+    paren = 0
+    previous_word = ''
+    for word in _r_words.findall(csource):
+        if word in look_for_words:
+            if word == ';':
+                if is_typedef:
+                    words_used.discard(previous_word)
+                    look_for_words.discard(previous_word)
+                    is_typedef = False
+            elif word == 'typedef':
+                is_typedef = True
+                paren = 0
+            elif word == '(':
+                paren += 1
+            elif word == ')':
+                paren -= 1
+            elif word == ',':
+                if is_typedef and paren == 0:
+                    words_used.discard(previous_word)
+                    look_for_words.discard(previous_word)
+            else:   # word in COMMON_TYPES
+                words_used.add(word)
+        previous_word = word
+    return words_used
+
+
+class Parser(object):
+
+    def __init__(self):
+        self._declarations = {}
+        self._included_declarations = set()
+        self._anonymous_counter = 0
+        self._structnode2type = weakref.WeakKeyDictionary()
+        self._options = {}
+        self._int_constants = {}
+        self._recomplete = []
+        self._uses_new_feature = None
+
+    def _parse(self, csource):
+        csource, macros = _preprocess(csource)
+        # XXX: for more efficiency we would need to poke into the
+        # internals of CParser...  the following registers the
+        # typedefs, because their presence or absence influences the
+        # parsing itself (but what they are typedef'ed to plays no role)
+        ctn = _common_type_names(csource)
+        typenames = []
+        for name in sorted(self._declarations):
+            if name.startswith('typedef '):
+                name = name[8:]
+                typenames.append(name)
+                ctn.discard(name)
+        typenames += sorted(ctn)
+        #
+        csourcelines = []
+        csourcelines.append('# 1 "<cdef automatic initialization code>"')
+        for typename in typenames:
+            csourcelines.append('typedef int %s;' % typename)
+        csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,'
+                            ' __dotdotdot__;')
+        # this forces pycparser to consider the following in the file
+        # called <cdef source string> from line 1
+        csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,))
+        csourcelines.append(csource)
+        csourcelines.append('')   # see test_missing_newline_bug
+        fullcsource = '\n'.join(csourcelines)
+        if lock is not None:
+            lock.acquire()     # pycparser is not thread-safe...
+        try:
+            ast = _get_parser().parse(fullcsource)
+        except pycparser.c_parser.ParseError as e:
+            self.convert_pycparser_error(e, csource)
+        finally:
+            if lock is not None:
+                lock.release()
+        # csource will be used to find buggy source text
+        return ast, macros, csource
+
+    def _convert_pycparser_error(self, e, csource):
+        # xxx look for "<cdef source string>:NUM:" at the start of str(e)
+        # and interpret that as a line number.  This will not work if
+        # the user gives explicit ``# NUM "FILE"`` directives.
+        line = None
+        msg = str(e)
+        match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg)
+        if match:
+            linenum = int(match.group(1), 10)
+            csourcelines = csource.splitlines()
+            if 1 <= linenum <= len(csourcelines):
+                line = csourcelines[linenum-1]
+        return line
+
+    def convert_pycparser_error(self, e, csource):
+        line = self._convert_pycparser_error(e, csource)
+
+        msg = str(e)
+        if line:
+            msg = 'cannot parse "%s"\n%s' % (line.strip(), msg)
+        else:
+            msg = 'parse error\n%s' % (msg,)
+        raise CDefError(msg)
+
+    def parse(self, csource, override=False, packed=False, pack=None,
+                    dllexport=False):
+        if packed:
+            if packed != True:
+                raise ValueError("'packed' should be False or True; use "
+                                 "'pack' to give another value")
+            if pack:
+                raise ValueError("cannot give both 'pack' and 'packed'")
+            pack = 1
+        elif pack:
+            if pack & (pack - 1):
+                raise ValueError("'pack' must be a power of two, not %r" %
+                    (pack,))
+        else:
+            pack = 0
+        prev_options = self._options
+        try:
+            self._options = {'override': override,
+                             'packed': pack,
+                             'dllexport': dllexport}
+            self._internal_parse(csource)
+        finally:
+            self._options = prev_options
+
+    def _internal_parse(self, csource):
+        ast, macros, csource = self._parse(csource)
+        # add the macros
+        self._process_macros(macros)
+        # find the first "__dotdotdot__" and use that as a separator
+        # between the repeated typedefs and the real csource
+        iterator = iter(ast.ext)
+        for decl in iterator:
+            if decl.name == '__dotdotdot__':
+                break
+        else:
+            assert 0
+        current_decl = None
+        #
+        try:
+            self._inside_extern_python = '__cffi_extern_python_stop'
+            for decl in iterator:
+                current_decl = decl
+                if isinstance(decl, pycparser.c_ast.Decl):
+                    self._parse_decl(decl)
+                elif isinstance(decl, pycparser.c_ast.Typedef):
+                    if not decl.name:
+                        raise CDefError("typedef does not declare any name",
+                                        decl)
+                    quals = 0
+                    if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and
+                            decl.type.type.names[-1].startswith('__dotdotdot')):
+                        realtype = self._get_unknown_type(decl)
+                    elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
+                          isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
+                          isinstance(decl.type.type.type,
+                                     pycparser.c_ast.IdentifierType) and
+                          decl.type.type.type.names[-1].startswith('__dotdotdot')):
+                        realtype = self._get_unknown_ptr_type(decl)
+                    else:
+                        realtype, quals = self._get_type_and_quals(
+                            decl.type, name=decl.name, partial_length_ok=True,
+                            typedef_example="*(%s *)0" % (decl.name,))
+                    self._declare('typedef ' + decl.name, realtype, quals=quals)
+                elif decl.__class__.__name__ == 'Pragma':
+                    # skip pragma, only in pycparser 2.15
+                    import warnings
+                    warnings.warn(
+                        "#pragma in cdef() are entirely ignored. "
+                        "They should be removed for now, otherwise your "
+                        "code might behave differently in a future version "
+                        "of CFFI if #pragma support gets added. Note that "
+                        "'#pragma pack' needs to be replaced with the "
+                        "'packed' keyword argument to cdef().")
+                else:
+                    raise CDefError("unexpected <%s>: this construct is valid "
+                                    "C but not valid in cdef()" %
+                                    decl.__class__.__name__, decl)
+        except CDefError as e:
+            if len(e.args) == 1:
+                e.args = e.args + (current_decl,)
+            raise
+        except FFIError as e:
+            msg = self._convert_pycparser_error(e, csource)
+            if msg:
+                e.args = (e.args[0] + "\n    *** Err: %s" % msg,)
+            raise
+
+    def _add_constants(self, key, val):
+        if key in self._int_constants:
+            if self._int_constants[key] == val:
+                return     # ignore identical double declarations
+            raise FFIError(
+                "multiple declarations of constant: %s" % (key,))
+        self._int_constants[key] = val
+
+    def _add_integer_constant(self, name, int_str):
+        int_str = int_str.lower().rstrip("ul")
+        neg = int_str.startswith('-')
+        if neg:
+            int_str = int_str[1:]
+        # "010" is not valid oct in py3
+        if (int_str.startswith("0") and int_str != '0'
+                and not int_str.startswith("0x")):
+            int_str = "0o" + int_str[1:]
+        pyvalue = int(int_str, 0)
+        if neg:
+            pyvalue = -pyvalue
+        self._add_constants(name, pyvalue)
+        self._declare('macro ' + name, pyvalue)
+
+    def _process_macros(self, macros):
+        for key, value in macros.items():
+            value = value.strip()
+            if _r_int_literal.match(value):
+                self._add_integer_constant(key, value)
+            elif value == '...':
+                self._declare('macro ' + key, value)
+            else:
+                raise CDefError(
+                    'only supports one of the following syntax:\n'
+                    '  #define %s ...     (literally dot-dot-dot)\n'
+                    '  #define %s NUMBER  (with NUMBER an integer'
+                                    ' constant, decimal/hex/octal)\n'
+                    'got:\n'
+                    '  #define %s %s'
+                    % (key, key, key, value))
+
+    def _declare_function(self, tp, quals, decl):
+        tp = self._get_type_pointer(tp, quals)
+        if self._options.get('dllexport'):
+            tag = 'dllexport_python '
+        elif self._inside_extern_python == '__cffi_extern_python_start':
+            tag = 'extern_python '
+        elif self._inside_extern_python == '__cffi_extern_python_plus_c_start':
+            tag = 'extern_python_plus_c '
+        else:
+            tag = 'function '
+        self._declare(tag + decl.name, tp)
+
+    def _parse_decl(self, decl):
+        node = decl.type
+        if isinstance(node, pycparser.c_ast.FuncDecl):
+            tp, quals = self._get_type_and_quals(node, name=decl.name)
+            assert isinstance(tp, model.RawFunctionType)
+            self._declare_function(tp, quals, decl)
+        else:
+            if isinstance(node, pycparser.c_ast.Struct):
+                self._get_struct_union_enum_type('struct', node)
+            elif isinstance(node, pycparser.c_ast.Union):
+                self._get_struct_union_enum_type('union', node)
+            elif isinstance(node, pycparser.c_ast.Enum):
+                self._get_struct_union_enum_type('enum', node)
+            elif not decl.name:
+                raise CDefError("construct does not declare any variable",
+                                decl)
+            #
+            if decl.name:
+                tp, quals = self._get_type_and_quals(node,
+                                                     partial_length_ok=True)
+                if tp.is_raw_function:
+                    self._declare_function(tp, quals, decl)
+                elif (tp.is_integer_type() and
+                        hasattr(decl, 'init') and
+                        hasattr(decl.init, 'value') and
+                        _r_int_literal.match(decl.init.value)):
+                    self._add_integer_constant(decl.name, decl.init.value)
+                elif (tp.is_integer_type() and
+                        isinstance(decl.init, pycparser.c_ast.UnaryOp) and
+                        decl.init.op == '-' and
+                        hasattr(decl.init.expr, 'value') and
+                        _r_int_literal.match(decl.init.expr.value)):
+                    self._add_integer_constant(decl.name,
+                                               '-' + decl.init.expr.value)
+                elif (tp is model.void_type and
+                      decl.name.startswith('__cffi_extern_python_')):
+                    # hack: `extern "Python"` in the C source is replaced
+                    # with "void __cffi_extern_python_start;" and
+                    # "void __cffi_extern_python_stop;"
+                    self._inside_extern_python = decl.name
+                else:
+                    if self._inside_extern_python !='__cffi_extern_python_stop':
+                        raise CDefError(
+                            "cannot declare constants or "
+                            "variables with 'extern \"Python\"'")
+                    if (quals & model.Q_CONST) and not tp.is_array_type:
+                        self._declare('constant ' + decl.name, tp, quals=quals)
+                    else:
+                        _warn_for_non_extern_non_static_global_variable(decl)
+                        self._declare('variable ' + decl.name, tp, quals=quals)
+
+    def parse_type(self, cdecl):
+        return self.parse_type_and_quals(cdecl)[0]
+
+    def parse_type_and_quals(self, cdecl):
+        ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2]
+        assert not macros
+        exprnode = ast.ext[-1].type.args.params[0]
+        if isinstance(exprnode, pycparser.c_ast.ID):
+            raise CDefError("unknown identifier '%s'" % (exprnode.name,))
+        return self._get_type_and_quals(exprnode.type)
+
+    def _declare(self, name, obj, included=False, quals=0):
+        if name in self._declarations:
+            prevobj, prevquals = self._declarations[name]
+            if prevobj is obj and prevquals == quals:
+                return
+            if not self._options.get('override'):
+                raise FFIError(
+                    "multiple declarations of %s (for interactive usage, "
+                    "try cdef(xx, override=True))" % (name,))
+        assert '__dotdotdot__' not in name.split()
+        self._declarations[name] = (obj, quals)
+        if included:
+            self._included_declarations.add(obj)
+
+    def _extract_quals(self, type):
+        quals = 0
+        if isinstance(type, (pycparser.c_ast.TypeDecl,
+                             pycparser.c_ast.PtrDecl)):
+            if 'const' in type.quals:
+                quals |= model.Q_CONST
+            if 'volatile' in type.quals:
+                quals |= model.Q_VOLATILE
+            if 'restrict' in type.quals:
+                quals |= model.Q_RESTRICT
+        return quals
+
+    def _get_type_pointer(self, type, quals, declname=None):
+        if isinstance(type, model.RawFunctionType):
+            return type.as_function_pointer()
+        if (isinstance(type, model.StructOrUnionOrEnum) and
+                type.name.startswith('$') and type.name[1:].isdigit() and
+                type.forcename is None and declname is not None):
+            return model.NamedPointerType(type, declname, quals)
+        return model.PointerType(type, quals)
+
+    def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False,
+                            typedef_example=None):
+        # first, dereference typedefs, if we have it already parsed, we're good
+        if (isinstance(typenode, pycparser.c_ast.TypeDecl) and
+            isinstance(typenode.type, pycparser.c_ast.IdentifierType) and
+            len(typenode.type.names) == 1 and
+            ('typedef ' + typenode.type.names[0]) in self._declarations):
+            tp, quals = self._declarations['typedef ' + typenode.type.names[0]]
+            quals |= self._extract_quals(typenode)
+            return tp, quals
+        #
+        if isinstance(typenode, pycparser.c_ast.ArrayDecl):
+            # array type
+            if typenode.dim is None:
+                length = None
+            else:
+                length = self._parse_constant(
+                    typenode.dim, partial_length_ok=partial_length_ok)
+            # a hack: in 'typedef int foo_t[...][...];', don't use '...' as
+            # the length but use directly the C expression that would be
+            # generated by recompiler.py.  This lets the typedef be used in
+            # many more places within recompiler.py
+            if typedef_example is not None:
+                if length == '...':
+                    length = '_cffi_array_len(%s)' % (typedef_example,)
+                typedef_example = "*" + typedef_example
+            #
+            tp, quals = self._get_type_and_quals(typenode.type,
+                                partial_length_ok=partial_length_ok,
+                                typedef_example=typedef_example)
+            return model.ArrayType(tp, length), quals
+        #
+        if isinstance(typenode, pycparser.c_ast.PtrDecl):
+            # pointer type
+            itemtype, itemquals = self._get_type_and_quals(typenode.type)
+            tp = self._get_type_pointer(itemtype, itemquals, declname=name)
+            quals = self._extract_quals(typenode)
+            return tp, quals
+        #
+        if isinstance(typenode, pycparser.c_ast.TypeDecl):
+            quals = self._extract_quals(typenode)
+            type = typenode.type
+            if isinstance(type, pycparser.c_ast.IdentifierType):
+                # assume a primitive type.  get it from .names, but reduce
+                # synonyms to a single chosen combination
+                names = list(type.names)
+                if names != ['signed', 'char']:    # keep this unmodified
+                    prefixes = {}
+                    while names:
+                        name = names[0]
+                        if name in ('short', 'long', 'signed', 'unsigned'):
+                            prefixes[name] = prefixes.get(name, 0) + 1
+                            del names[0]
+                        else:
+                            break
+                    # ignore the 'signed' prefix below, and reorder the others
+                    newnames = []
+                    for prefix in ('unsigned', 'short', 'long'):
+                        for i in range(prefixes.get(prefix, 0)):
+                            newnames.append(prefix)
+                    if not names:
+                        names = ['int']    # implicitly
+                    if names == ['int']:   # but kill it if 'short' or 'long'
+                        if 'short' in prefixes or 'long' in prefixes:
+                            names = []
+                    names = newnames + names
+                ident = ' '.join(names)
+                if ident == 'void':
+                    return model.void_type, quals
+                if ident == '__dotdotdot__':
+                    raise FFIError(':%d: bad usage of "..."' %
+                            typenode.coord.line)
+                tp0, quals0 = resolve_common_type(self, ident)
+                return tp0, (quals | quals0)
+            #
+            if isinstance(type, pycparser.c_ast.Struct):
+                # 'struct foobar'
+                tp = self._get_struct_union_enum_type('struct', type, name)
+                return tp, quals
+            #
+            if isinstance(type, pycparser.c_ast.Union):
+                # 'union foobar'
+                tp = self._get_struct_union_enum_type('union', type, name)
+                return tp, quals
+            #
+            if isinstance(type, pycparser.c_ast.Enum):
+                # 'enum foobar'
+                tp = self._get_struct_union_enum_type('enum', type, name)
+                return tp, quals
+        #
+        if isinstance(typenode, pycparser.c_ast.FuncDecl):
+            # a function type
+            return self._parse_function_type(typenode, name), 0
+        #
+        # nested anonymous structs or unions end up here
+        if isinstance(typenode, pycparser.c_ast.Struct):
+            return self._get_struct_union_enum_type('struct', typenode, name,
+                                                    nested=True), 0
+        if isinstance(typenode, pycparser.c_ast.Union):
+            return self._get_struct_union_enum_type('union', typenode, name,
+                                                    nested=True), 0
+        #
+        raise FFIError(":%d: bad or unsupported type declaration" %
+                typenode.coord.line)
+
+    def _parse_function_type(self, typenode, funcname=None):
+        params = list(getattr(typenode.args, 'params', []))
+        for i, arg in enumerate(params):
+            if not hasattr(arg, 'type'):
+                raise CDefError("%s arg %d: unknown type '%s'"
+                    " (if you meant to use the old C syntax of giving"
+                    " untyped arguments, it is not supported)"
+                    % (funcname or 'in expression', i + 1,
+                       getattr(arg, 'name', '?')))
+        ellipsis = (
+            len(params) > 0 and
+            isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and
+            isinstance(params[-1].type.type,
+                       pycparser.c_ast.IdentifierType) and
+            params[-1].type.type.names == ['__dotdotdot__'])
+        if ellipsis:
+            params.pop()
+            if not params:
+                raise CDefError(
+                    "%s: a function with only '(...)' as argument"
+                    " is not correct C" % (funcname or 'in expression'))
+        args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type))
+                for argdeclnode in params]
+        if not ellipsis and args == [model.void_type]:
+            args = []
+        result, quals = self._get_type_and_quals(typenode.type)
+        # the 'quals' on the result type are ignored.  HACK: we absure them
+        # to detect __stdcall functions: we textually replace "__stdcall"
+        # with "volatile volatile const" above.
+        abi = None
+        if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway
+            if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']:
+                abi = '__stdcall'
+        return model.RawFunctionType(tuple(args), result, ellipsis, abi)
+
+    def _as_func_arg(self, type, quals):
+        if isinstance(type, model.ArrayType):
+            return model.PointerType(type.item, quals)
+        elif isinstance(type, model.RawFunctionType):
+            return type.as_function_pointer()
+        else:
+            return type
+
+    def _get_struct_union_enum_type(self, kind, type, name=None, nested=False):
+        # First, a level of caching on the exact 'type' node of the AST.
+        # This is obscure, but needed because pycparser "unrolls" declarations
+        # such as "typedef struct { } foo_t, *foo_p" and we end up with
+        # an AST that is not a tree, but a DAG, with the "type" node of the
+        # two branches foo_t and foo_p of the trees being the same node.
+        # It's a bit silly but detecting "DAG-ness" in the AST tree seems
+        # to be the only way to distinguish this case from two independent
+        # structs.  See test_struct_with_two_usages.
+        try:
+            return self._structnode2type[type]
+        except KeyError:
+            pass
+        #
+        # Note that this must handle parsing "struct foo" any number of
+        # times and always return the same StructType object.  Additionally,
+        # one of these times (not necessarily the first), the fields of
+        # the struct can be specified with "struct foo { ...fields... }".
+        # If no name is given, then we have to create a new anonymous struct
+        # with no caching; in this case, the fields are either specified
+        # right now or never.
+        #
+        force_name = name
+        name = type.name
+        #
+        # get the type or create it if needed
+        if name is None:
+            # 'force_name' is used to guess a more readable name for
+            # anonymous structs, for the common case "typedef struct { } foo".
+            if force_name is not None:
+                explicit_name = '$%s' % force_name
+            else:
+                self._anonymous_counter += 1
+                explicit_name = '$%d' % self._anonymous_counter
+            tp = None
+        else:
+            explicit_name = name
+            key = '%s %s' % (kind, name)
+            tp, _ = self._declarations.get(key, (None, None))
+        #
+        if tp is None:
+            if kind == 'struct':
+                tp = model.StructType(explicit_name, None, None, None)
+            elif kind == 'union':
+                tp = model.UnionType(explicit_name, None, None, None)
+            elif kind == 'enum':
+                if explicit_name == '__dotdotdot__':
+                    raise CDefError("Enums cannot be declared with ...")
+                tp = self._build_enum_type(explicit_name, type.values)
+            else:
+                raise AssertionError("kind = %r" % (kind,))
+            if name is not None:
+                self._declare(key, tp)
+        else:
+            if kind == 'enum' and type.values is not None:
+                raise NotImplementedError(
+                    "enum %s: the '{}' declaration should appear on the first "
+                    "time the enum is mentioned, not later" % explicit_name)
+        if not tp.forcename:
+            tp.force_the_name(force_name)
+        if tp.forcename and '$' in tp.name:
+            self._declare('anonymous %s' % tp.forcename, tp)
+        #
+        self._structnode2type[type] = tp
+        #
+        # enums: done here
+        if kind == 'enum':
+            return tp
+        #
+        # is there a 'type.decls'?  If yes, then this is the place in the
+        # C sources that declare the fields.  If no, then just return the
+        # existing type, possibly still incomplete.
+        if type.decls is None:
+            return tp
+        #
+        if tp.fldnames is not None:
+            raise CDefError("duplicate declaration of struct %s" % name)
+        fldnames = []
+        fldtypes = []
+        fldbitsize = []
+        fldquals = []
+        for decl in type.decls:
+            if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and
+                    ''.join(decl.type.names) == '__dotdotdot__'):
+                # XXX pycparser is inconsistent: 'names' should be a list
+                # of strings, but is sometimes just one string.  Use
+                # str.join() as a way to cope with both.
+                self._make_partial(tp, nested)
+                continue
+            if decl.bitsize is None:
+                bitsize = -1
+            else:
+                bitsize = self._parse_constant(decl.bitsize)
+            self._partial_length = False
+            type, fqual = self._get_type_and_quals(decl.type,
+                                                   partial_length_ok=True)
+            if self._partial_length:
+                self._make_partial(tp, nested)
+            if isinstance(type, model.StructType) and type.partial:
+                self._make_partial(tp, nested)
+            fldnames.append(decl.name or '')
+            fldtypes.append(type)
+            fldbitsize.append(bitsize)
+            fldquals.append(fqual)
+        tp.fldnames = tuple(fldnames)
+        tp.fldtypes = tuple(fldtypes)
+        tp.fldbitsize = tuple(fldbitsize)
+        tp.fldquals = tuple(fldquals)
+        if fldbitsize != [-1] * len(fldbitsize):
+            if isinstance(tp, model.StructType) and tp.partial:
+                raise NotImplementedError("%s: using both bitfields and '...;'"
+                                          % (tp,))
+        tp.packed = self._options.get('packed')
+        if tp.completed:    # must be re-completed: it is not opaque any more
+            tp.completed = 0
+            self._recomplete.append(tp)
+        return tp
+
+    def _make_partial(self, tp, nested):
+        if not isinstance(tp, model.StructOrUnion):
+            raise CDefError("%s cannot be partial" % (tp,))
+        if not tp.has_c_name() and not nested:
+            raise NotImplementedError("%s is partial but has no C name" %(tp,))
+        tp.partial = True
+
+    def _parse_constant(self, exprnode, partial_length_ok=False):
+        # for now, limited to expressions that are an immediate number
+        # or positive/negative number
+        if isinstance(exprnode, pycparser.c_ast.Constant):
+            s = exprnode.value
+            if '0' <= s[0] <= '9':
+                s = s.rstrip('uUlL')
+                try:
+                    if s.startswith('0'):
+                        return int(s, 8)
+                    else:
+                        return int(s, 10)
+                except ValueError:
+                    if len(s) > 1:
+                        if s.lower()[0:2] == '0x':
+                            return int(s, 16)
+                        elif s.lower()[0:2] == '0b':
+                            return int(s, 2)
+                raise CDefError("invalid constant %r" % (s,))
+            elif s[0] == "'" and s[-1] == "'" and (
+                    len(s) == 3 or (len(s) == 4 and s[1] == "\\")):
+                return ord(s[-2])
+            else:
+                raise CDefError("invalid constant %r" % (s,))
+        #
+        if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
+                exprnode.op == '+'):
+            return self._parse_constant(exprnode.expr)
+        #
+        if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
+                exprnode.op == '-'):
+            return -self._parse_constant(exprnode.expr)
+        # load previously defined int constant
+        if (isinstance(exprnode, pycparser.c_ast.ID) and
+                exprnode.name in self._int_constants):
+            return self._int_constants[exprnode.name]
+        #
+        if (isinstance(exprnode, pycparser.c_ast.ID) and
+                    exprnode.name == '__dotdotdotarray__'):
+            if partial_length_ok:
+                self._partial_length = True
+                return '...'
+            raise FFIError(":%d: unsupported '[...]' here, cannot derive "
+                           "the actual array length in this context"
+                           % exprnode.coord.line)
+        #
+        if isinstance(exprnode, pycparser.c_ast.BinaryOp):
+            left = self._parse_constant(exprnode.left)
+            right = self._parse_constant(exprnode.right)
+            if exprnode.op == '+':
+                return left + right
+            elif exprnode.op == '-':
+                return left - right
+            elif exprnode.op == '*':
+                return left * right
+            elif exprnode.op == '/':
+                return self._c_div(left, right)
+            elif exprnode.op == '%':
+                return left - self._c_div(left, right) * right
+            elif exprnode.op == '<<':
+                return left << right
+            elif exprnode.op == '>>':
+                return left >> right
+            elif exprnode.op == '&':
+                return left & right
+            elif exprnode.op == '|':
+                return left | right
+            elif exprnode.op == '^':
+                return left ^ right
+        #
+        raise FFIError(":%d: unsupported expression: expected a "
+                       "simple numeric constant" % exprnode.coord.line)
+
+    def _c_div(self, a, b):
+        result = a // b
+        if ((a < 0) ^ (b < 0)) and (a % b) != 0:
+            result += 1
+        return result
+
+    def _build_enum_type(self, explicit_name, decls):
+        if decls is not None:
+            partial = False
+            enumerators = []
+            enumvalues = []
+            nextenumvalue = 0
+            for enum in decls.enumerators:
+                if _r_enum_dotdotdot.match(enum.name):
+                    partial = True
+                    continue
+                if enum.value is not None:
+                    nextenumvalue = self._parse_constant(enum.value)
+                enumerators.append(enum.name)
+                enumvalues.append(nextenumvalue)
+                self._add_constants(enum.name, nextenumvalue)
+                nextenumvalue += 1
+            enumerators = tuple(enumerators)
+            enumvalues = tuple(enumvalues)
+            tp = model.EnumType(explicit_name, enumerators, enumvalues)
+            tp.partial = partial
+        else:   # opaque enum
+            tp = model.EnumType(explicit_name, (), ())
+        return tp
+
+    def include(self, other):
+        for name, (tp, quals) in other._declarations.items():
+            if name.startswith('anonymous $enum_$'):
+                continue   # fix for test_anonymous_enum_include
+            kind = name.split(' ', 1)[0]
+            if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'):
+                self._declare(name, tp, included=True, quals=quals)
+        for k, v in other._int_constants.items():
+            self._add_constants(k, v)
+
+    def _get_unknown_type(self, decl):
+        typenames = decl.type.type.names
+        if typenames == ['__dotdotdot__']:
+            return model.unknown_type(decl.name)
+
+        if typenames == ['__dotdotdotint__']:
+            if self._uses_new_feature is None:
+                self._uses_new_feature = "'typedef int... %s'" % decl.name
+            return model.UnknownIntegerType(decl.name)
+
+        if typenames == ['__dotdotdotfloat__']:
+            # note: not for 'long double' so far
+            if self._uses_new_feature is None:
+                self._uses_new_feature = "'typedef float... %s'" % decl.name
+            return model.UnknownFloatType(decl.name)
+
+        raise FFIError(':%d: unsupported usage of "..." in typedef'
+                       % decl.coord.line)
+
+    def _get_unknown_ptr_type(self, decl):
+        if decl.type.type.type.names == ['__dotdotdot__']:
+            return model.unknown_ptr_type(decl.name)
+        raise FFIError(':%d: unsupported usage of "..." in typedef'
+                       % decl.coord.line)
diff --git a/.venv/lib/python3.8/site-packages/cffi/error.py b/.venv/lib/python3.8/site-packages/cffi/error.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a27247c32a381ab7cecedd0f985b781619c1ea5
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/error.py
@@ -0,0 +1,31 @@
+
+class FFIError(Exception):
+    __module__ = 'cffi'
+
+class CDefError(Exception):
+    __module__ = 'cffi'
+    def __str__(self):
+        try:
+            current_decl = self.args[1]
+            filename = current_decl.coord.file
+            linenum = current_decl.coord.line
+            prefix = '%s:%d: ' % (filename, linenum)
+        except (AttributeError, TypeError, IndexError):
+            prefix = ''
+        return '%s%s' % (prefix, self.args[0])
+
+class VerificationError(Exception):
+    """ An error raised when verification fails
+    """
+    __module__ = 'cffi'
+
+class VerificationMissing(Exception):
+    """ An error raised when incomplete structures are passed into
+    cdef, but no verification has been done
+    """
+    __module__ = 'cffi'
+
+class PkgConfigError(Exception):
+    """ An error raised for missing modules in pkg-config
+    """
+    __module__ = 'cffi'
diff --git a/.venv/lib/python3.8/site-packages/cffi/ffiplatform.py b/.venv/lib/python3.8/site-packages/cffi/ffiplatform.py
new file mode 100644
index 0000000000000000000000000000000000000000..adca28f1a480bb04a11977d26457fe8886139043
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/ffiplatform.py
@@ -0,0 +1,113 @@
+import sys, os
+from .error import VerificationError
+
+
+LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs',
+                      'extra_objects', 'depends']
+
+def get_extension(srcfilename, modname, sources=(), **kwds):
+    from cffi._shimmed_dist_utils import Extension
+    allsources = [srcfilename]
+    for src in sources:
+        allsources.append(os.path.normpath(src))
+    return Extension(name=modname, sources=allsources, **kwds)
+
+def compile(tmpdir, ext, compiler_verbose=0, debug=None):
+    """Compile a C extension module using distutils."""
+
+    saved_environ = os.environ.copy()
+    try:
+        outputfilename = _build(tmpdir, ext, compiler_verbose, debug)
+        outputfilename = os.path.abspath(outputfilename)
+    finally:
+        # workaround for a distutils bugs where some env vars can
+        # become longer and longer every time it is used
+        for key, value in saved_environ.items():
+            if os.environ.get(key) != value:
+                os.environ[key] = value
+    return outputfilename
+
+def _build(tmpdir, ext, compiler_verbose=0, debug=None):
+    # XXX compact but horrible :-(
+    from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity
+
+    dist = Distribution({'ext_modules': [ext]})
+    dist.parse_config_files()
+    options = dist.get_option_dict('build_ext')
+    if debug is None:
+        debug = sys.flags.debug
+    options['debug'] = ('ffiplatform', debug)
+    options['force'] = ('ffiplatform', True)
+    options['build_lib'] = ('ffiplatform', tmpdir)
+    options['build_temp'] = ('ffiplatform', tmpdir)
+    #
+    try:
+        old_level = set_threshold(0) or 0
+        try:
+            set_verbosity(compiler_verbose)
+            dist.run_command('build_ext')
+            cmd_obj = dist.get_command_obj('build_ext')
+            [soname] = cmd_obj.get_outputs()
+        finally:
+            set_threshold(old_level)
+    except (CompileError, LinkError) as e:
+        raise VerificationError('%s: %s' % (e.__class__.__name__, e))
+    #
+    return soname
+
+try:
+    from os.path import samefile
+except ImportError:
+    def samefile(f1, f2):
+        return os.path.abspath(f1) == os.path.abspath(f2)
+
+def maybe_relative_path(path):
+    if not os.path.isabs(path):
+        return path      # already relative
+    dir = path
+    names = []
+    while True:
+        prevdir = dir
+        dir, name = os.path.split(prevdir)
+        if dir == prevdir or not dir:
+            return path     # failed to make it relative
+        names.append(name)
+        try:
+            if samefile(dir, os.curdir):
+                names.reverse()
+                return os.path.join(*names)
+        except OSError:
+            pass
+
+# ____________________________________________________________
+
+try:
+    int_or_long = (int, long)
+    import cStringIO
+except NameError:
+    int_or_long = int      # Python 3
+    import io as cStringIO
+
+def _flatten(x, f):
+    if isinstance(x, str):
+        f.write('%ds%s' % (len(x), x))
+    elif isinstance(x, dict):
+        keys = sorted(x.keys())
+        f.write('%dd' % len(keys))
+        for key in keys:
+            _flatten(key, f)
+            _flatten(x[key], f)
+    elif isinstance(x, (list, tuple)):
+        f.write('%dl' % len(x))
+        for value in x:
+            _flatten(value, f)
+    elif isinstance(x, int_or_long):
+        f.write('%di' % (x,))
+    else:
+        raise TypeError(
+            "the keywords to verify() contains unsupported object %r" % (x,))
+
+def flatten(x):
+    f = cStringIO.StringIO()
+    _flatten(x, f)
+    return f.getvalue()
diff --git a/.venv/lib/python3.8/site-packages/cffi/lock.py b/.venv/lib/python3.8/site-packages/cffi/lock.py
new file mode 100644
index 0000000000000000000000000000000000000000..db91b7158c4ee9aa653462fe38e79ed1b553db87
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/lock.py
@@ -0,0 +1,30 @@
+import sys
+
+if sys.version_info < (3,):
+    try:
+        from thread import allocate_lock
+    except ImportError:
+        from dummy_thread import allocate_lock
+else:
+    try:
+        from _thread import allocate_lock
+    except ImportError:
+        from _dummy_thread import allocate_lock
+
+
+##import sys
+##l1 = allocate_lock
+
+##class allocate_lock(object):
+##    def __init__(self):
+##        self._real = l1()
+##    def __enter__(self):
+##        for i in range(4, 0, -1):
+##            print sys._getframe(i).f_code
+##        print
+##        return self._real.__enter__()
+##    def __exit__(self, *args):
+##        return self._real.__exit__(*args)
+##    def acquire(self, f):
+##        assert f is False
+##        return self._real.acquire(f)
diff --git a/.venv/lib/python3.8/site-packages/cffi/model.py b/.venv/lib/python3.8/site-packages/cffi/model.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5f4cae3e84c73cd09980dabd2ec571d455fe0c1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/model.py
@@ -0,0 +1,618 @@
+import types
+import weakref
+
+from .lock import allocate_lock
+from .error import CDefError, VerificationError, VerificationMissing
+
+# type qualifiers
+Q_CONST    = 0x01
+Q_RESTRICT = 0x02
+Q_VOLATILE = 0x04
+
+def qualify(quals, replace_with):
+    if quals & Q_CONST:
+        replace_with = ' const ' + replace_with.lstrip()
+    if quals & Q_VOLATILE:
+        replace_with = ' volatile ' + replace_with.lstrip()
+    if quals & Q_RESTRICT:
+        # It seems that __restrict is supported by gcc and msvc.
+        # If you hit some different compiler, add a #define in
+        # _cffi_include.h for it (and in its copies, documented there)
+        replace_with = ' __restrict ' + replace_with.lstrip()
+    return replace_with
+
+
+class BaseTypeByIdentity(object):
+    is_array_type = False
+    is_raw_function = False
+
+    def get_c_name(self, replace_with='', context='a C file', quals=0):
+        result = self.c_name_with_marker
+        assert result.count('&') == 1
+        # some logic duplication with ffi.getctype()... :-(
+        replace_with = replace_with.strip()
+        if replace_with:
+            if replace_with.startswith('*') and '&[' in result:
+                replace_with = '(%s)' % replace_with
+            elif not replace_with[0] in '[(':
+                replace_with = ' ' + replace_with
+        replace_with = qualify(quals, replace_with)
+        result = result.replace('&', replace_with)
+        if '$' in result:
+            raise VerificationError(
+                "cannot generate '%s' in %s: unknown type name"
+                % (self._get_c_name(), context))
+        return result
+
+    def _get_c_name(self):
+        return self.c_name_with_marker.replace('&', '')
+
+    def has_c_name(self):
+        return '$' not in self._get_c_name()
+
+    def is_integer_type(self):
+        return False
+
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        try:
+            BType = ffi._cached_btypes[self]
+        except KeyError:
+            BType = self.build_backend_type(ffi, finishlist)
+            BType2 = ffi._cached_btypes.setdefault(self, BType)
+            assert BType2 is BType
+        return BType
+
+    def __repr__(self):
+        return '<%s>' % (self._get_c_name(),)
+
+    def _get_items(self):
+        return [(name, getattr(self, name)) for name in self._attrs_]
+
+
+class BaseType(BaseTypeByIdentity):
+
+    def __eq__(self, other):
+        return (self.__class__ == other.__class__ and
+                self._get_items() == other._get_items())
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __hash__(self):
+        return hash((self.__class__, tuple(self._get_items())))
+
+
+class VoidType(BaseType):
+    _attrs_ = ()
+
+    def __init__(self):
+        self.c_name_with_marker = 'void&'
+
+    def build_backend_type(self, ffi, finishlist):
+        return global_cache(self, ffi, 'new_void_type')
+
+void_type = VoidType()
+
+
+class BasePrimitiveType(BaseType):
+    def is_complex_type(self):
+        return False
+
+
+class PrimitiveType(BasePrimitiveType):
+    _attrs_ = ('name',)
+
+    ALL_PRIMITIVE_TYPES = {
+        'char':               'c',
+        'short':              'i',
+        'int':                'i',
+        'long':               'i',
+        'long long':          'i',
+        'signed char':        'i',
+        'unsigned char':      'i',
+        'unsigned short':     'i',
+        'unsigned int':       'i',
+        'unsigned long':      'i',
+        'unsigned long long': 'i',
+        'float':              'f',
+        'double':             'f',
+        'long double':        'f',
+        '_cffi_float_complex_t': 'j',
+        '_cffi_double_complex_t': 'j',
+        '_Bool':              'i',
+        # the following types are not primitive in the C sense
+        'wchar_t':            'c',
+        'char16_t':           'c',
+        'char32_t':           'c',
+        'int8_t':             'i',
+        'uint8_t':            'i',
+        'int16_t':            'i',
+        'uint16_t':           'i',
+        'int32_t':            'i',
+        'uint32_t':           'i',
+        'int64_t':            'i',
+        'uint64_t':           'i',
+        'int_least8_t':       'i',
+        'uint_least8_t':      'i',
+        'int_least16_t':      'i',
+        'uint_least16_t':     'i',
+        'int_least32_t':      'i',
+        'uint_least32_t':     'i',
+        'int_least64_t':      'i',
+        'uint_least64_t':     'i',
+        'int_fast8_t':        'i',
+        'uint_fast8_t':       'i',
+        'int_fast16_t':       'i',
+        'uint_fast16_t':      'i',
+        'int_fast32_t':       'i',
+        'uint_fast32_t':      'i',
+        'int_fast64_t':       'i',
+        'uint_fast64_t':      'i',
+        'intptr_t':           'i',
+        'uintptr_t':          'i',
+        'intmax_t':           'i',
+        'uintmax_t':          'i',
+        'ptrdiff_t':          'i',
+        'size_t':             'i',
+        'ssize_t':            'i',
+        }
+
+    def __init__(self, name):
+        assert name in self.ALL_PRIMITIVE_TYPES
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def is_char_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'c'
+    def is_integer_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
+    def is_float_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
+    def is_complex_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'j'
+
+    def build_backend_type(self, ffi, finishlist):
+        return global_cache(self, ffi, 'new_primitive_type', self.name)
+
+
+class UnknownIntegerType(BasePrimitiveType):
+    _attrs_ = ('name',)
+
+    def __init__(self, name):
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def is_integer_type(self):
+        return True
+
+    def build_backend_type(self, ffi, finishlist):
+        raise NotImplementedError("integer type '%s' can only be used after "
+                                  "compilation" % self.name)
+
+class UnknownFloatType(BasePrimitiveType):
+    _attrs_ = ('name', )
+
+    def __init__(self, name):
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def build_backend_type(self, ffi, finishlist):
+        raise NotImplementedError("float type '%s' can only be used after "
+                                  "compilation" % self.name)
+
+
+class BaseFunctionType(BaseType):
+    _attrs_ = ('args', 'result', 'ellipsis', 'abi')
+
+    def __init__(self, args, result, ellipsis, abi=None):
+        self.args = args
+        self.result = result
+        self.ellipsis = ellipsis
+        self.abi = abi
+        #
+        reprargs = [arg._get_c_name() for arg in self.args]
+        if self.ellipsis:
+            reprargs.append('...')
+        reprargs = reprargs or ['void']
+        replace_with = self._base_pattern % (', '.join(reprargs),)
+        if abi is not None:
+            replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]
+        self.c_name_with_marker = (
+            self.result.c_name_with_marker.replace('&', replace_with))
+
+
+class RawFunctionType(BaseFunctionType):
+    # Corresponds to a C type like 'int(int)', which is the C type of
+    # a function, but not a pointer-to-function.  The backend has no
+    # notion of such a type; it's used temporarily by parsing.
+    _base_pattern = '(&)(%s)'
+    is_raw_function = True
+
+    def build_backend_type(self, ffi, finishlist):
+        raise CDefError("cannot render the type %r: it is a function "
+                        "type, not a pointer-to-function type" % (self,))
+
+    def as_function_pointer(self):
+        return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)
+
+
+class FunctionPtrType(BaseFunctionType):
+    _base_pattern = '(*&)(%s)'
+
+    def build_backend_type(self, ffi, finishlist):
+        result = self.result.get_cached_btype(ffi, finishlist)
+        args = []
+        for tp in self.args:
+            args.append(tp.get_cached_btype(ffi, finishlist))
+        abi_args = ()
+        if self.abi == "__stdcall":
+            if not self.ellipsis:    # __stdcall ignored for variadic funcs
+                try:
+                    abi_args = (ffi._backend.FFI_STDCALL,)
+                except AttributeError:
+                    pass
+        return global_cache(self, ffi, 'new_function_type',
+                            tuple(args), result, self.ellipsis, *abi_args)
+
+    def as_raw_function(self):
+        return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)
+
+
+class PointerType(BaseType):
+    _attrs_ = ('totype', 'quals')
+
+    def __init__(self, totype, quals=0):
+        self.totype = totype
+        self.quals = quals
+        extra = " *&"
+        if totype.is_array_type:
+            extra = "(%s)" % (extra.lstrip(),)
+        extra = qualify(quals, extra)
+        self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)
+
+    def build_backend_type(self, ffi, finishlist):
+        BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
+        return global_cache(self, ffi, 'new_pointer_type', BItem)
+
+voidp_type = PointerType(void_type)
+
+def ConstPointerType(totype):
+    return PointerType(totype, Q_CONST)
+
+const_voidp_type = ConstPointerType(void_type)
+
+
+class NamedPointerType(PointerType):
+    _attrs_ = ('totype', 'name')
+
+    def __init__(self, totype, name, quals=0):
+        PointerType.__init__(self, totype, quals)
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+
+class ArrayType(BaseType):
+    _attrs_ = ('item', 'length')
+    is_array_type = True
+
+    def __init__(self, item, length):
+        self.item = item
+        self.length = length
+        #
+        if length is None:
+            brackets = '&[]'
+        elif length == '...':
+            brackets = '&[/*...*/]'
+        else:
+            brackets = '&[%s]' % length
+        self.c_name_with_marker = (
+            self.item.c_name_with_marker.replace('&', brackets))
+
+    def length_is_unknown(self):
+        return isinstance(self.length, str)
+
+    def resolve_length(self, newlength):
+        return ArrayType(self.item, newlength)
+
+    def build_backend_type(self, ffi, finishlist):
+        if self.length_is_unknown():
+            raise CDefError("cannot render the type %r: unknown length" %
+                            (self,))
+        self.item.get_cached_btype(ffi, finishlist)   # force the item BType
+        BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
+        return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
+
+char_array_type = ArrayType(PrimitiveType('char'), None)
+
+
+class StructOrUnionOrEnum(BaseTypeByIdentity):
+    _attrs_ = ('name',)
+    forcename = None
+
+    def build_c_name_with_marker(self):
+        name = self.forcename or '%s %s' % (self.kind, self.name)
+        self.c_name_with_marker = name + '&'
+
+    def force_the_name(self, forcename):
+        self.forcename = forcename
+        self.build_c_name_with_marker()
+
+    def get_official_name(self):
+        assert self.c_name_with_marker.endswith('&')
+        return self.c_name_with_marker[:-1]
+
+
+class StructOrUnion(StructOrUnionOrEnum):
+    fixedlayout = None
+    completed = 0
+    partial = False
+    packed = 0
+
+    def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None):
+        self.name = name
+        self.fldnames = fldnames
+        self.fldtypes = fldtypes
+        self.fldbitsize = fldbitsize
+        self.fldquals = fldquals
+        self.build_c_name_with_marker()
+
+    def anonymous_struct_fields(self):
+        if self.fldtypes is not None:
+            for name, type in zip(self.fldnames, self.fldtypes):
+                if name == '' and isinstance(type, StructOrUnion):
+                    yield type
+
+    def enumfields(self, expand_anonymous_struct_union=True):
+        fldquals = self.fldquals
+        if fldquals is None:
+            fldquals = (0,) * len(self.fldnames)
+        for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
+                                              self.fldbitsize, fldquals):
+            if (name == '' and isinstance(type, StructOrUnion)
+                    and expand_anonymous_struct_union):
+                # nested anonymous struct/union
+                for result in type.enumfields():
+                    yield result
+            else:
+                yield (name, type, bitsize, quals)
+
+    def force_flatten(self):
+        # force the struct or union to have a declaration that lists
+        # directly all fields returned by enumfields(), flattening
+        # nested anonymous structs/unions.
+        names = []
+        types = []
+        bitsizes = []
+        fldquals = []
+        for name, type, bitsize, quals in self.enumfields():
+            names.append(name)
+            types.append(type)
+            bitsizes.append(bitsize)
+            fldquals.append(quals)
+        self.fldnames = tuple(names)
+        self.fldtypes = tuple(types)
+        self.fldbitsize = tuple(bitsizes)
+        self.fldquals = tuple(fldquals)
+
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
+                                                     can_delay)
+        if not can_delay:
+            self.finish_backend_type(ffi, finishlist)
+        return BType
+
+    def finish_backend_type(self, ffi, finishlist):
+        if self.completed:
+            if self.completed != 2:
+                raise NotImplementedError("recursive structure declaration "
+                                          "for '%s'" % (self.name,))
+            return
+        BType = ffi._cached_btypes[self]
+        #
+        self.completed = 1
+        #
+        if self.fldtypes is None:
+            pass    # not completing it: it's an opaque struct
+            #
+        elif self.fixedlayout is None:
+            fldtypes = [tp.get_cached_btype(ffi, finishlist)
+                        for tp in self.fldtypes]
+            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
+            extra_flags = ()
+            if self.packed:
+                if self.packed == 1:
+                    extra_flags = (8,)    # SF_PACKED
+                else:
+                    extra_flags = (0, self.packed)
+            ffi._backend.complete_struct_or_union(BType, lst, self,
+                                                  -1, -1, *extra_flags)
+            #
+        else:
+            fldtypes = []
+            fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
+            for i in range(len(self.fldnames)):
+                fsize = fieldsize[i]
+                ftype = self.fldtypes[i]
+                #
+                if isinstance(ftype, ArrayType) and ftype.length_is_unknown():
+                    # fix the length to match the total size
+                    BItemType = ftype.item.get_cached_btype(ffi, finishlist)
+                    nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
+                    if nrest != 0:
+                        self._verification_error(
+                            "field '%s.%s' has a bogus size?" % (
+                            self.name, self.fldnames[i] or '{}'))
+                    ftype = ftype.resolve_length(nlen)
+                    self.fldtypes = (self.fldtypes[:i] + (ftype,) +
+                                     self.fldtypes[i+1:])
+                #
+                BFieldType = ftype.get_cached_btype(ffi, finishlist)
+                if isinstance(ftype, ArrayType) and ftype.length is None:
+                    assert fsize == 0
+                else:
+                    bitemsize = ffi.sizeof(BFieldType)
+                    if bitemsize != fsize:
+                        self._verification_error(
+                            "field '%s.%s' is declared as %d bytes, but is "
+                            "really %d bytes" % (self.name,
+                                                 self.fldnames[i] or '{}',
+                                                 bitemsize, fsize))
+                fldtypes.append(BFieldType)
+            #
+            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
+            ffi._backend.complete_struct_or_union(BType, lst, self,
+                                                  totalsize, totalalignment)
+        self.completed = 2
+
+    def _verification_error(self, msg):
+        raise VerificationError(msg)
+
+    def check_not_partial(self):
+        if self.partial and self.fixedlayout is None:
+            raise VerificationMissing(self._get_c_name())
+
+    def build_backend_type(self, ffi, finishlist):
+        self.check_not_partial()
+        finishlist.append(self)
+        #
+        return global_cache(self, ffi, 'new_%s_type' % self.kind,
+                            self.get_official_name(), key=self)
+
+
+class StructType(StructOrUnion):
+    kind = 'struct'
+
+
+class UnionType(StructOrUnion):
+    kind = 'union'
+
+
+class EnumType(StructOrUnionOrEnum):
+    kind = 'enum'
+    partial = False
+    partial_resolved = False
+
+    def __init__(self, name, enumerators, enumvalues, baseinttype=None):
+        self.name = name
+        self.enumerators = enumerators
+        self.enumvalues = enumvalues
+        self.baseinttype = baseinttype
+        self.build_c_name_with_marker()
+
+    def force_the_name(self, forcename):
+        StructOrUnionOrEnum.force_the_name(self, forcename)
+        if self.forcename is None:
+            name = self.get_official_name()
+            self.forcename = '$' + name.replace(' ', '_')
+
+    def check_not_partial(self):
+        if self.partial and not self.partial_resolved:
+            raise VerificationMissing(self._get_c_name())
+
+    def build_backend_type(self, ffi, finishlist):
+        self.check_not_partial()
+        base_btype = self.build_baseinttype(ffi, finishlist)
+        return global_cache(self, ffi, 'new_enum_type',
+                            self.get_official_name(),
+                            self.enumerators, self.enumvalues,
+                            base_btype, key=self)
+
+    def build_baseinttype(self, ffi, finishlist):
+        if self.baseinttype is not None:
+            return self.baseinttype.get_cached_btype(ffi, finishlist)
+        #
+        if self.enumvalues:
+            smallest_value = min(self.enumvalues)
+            largest_value = max(self.enumvalues)
+        else:
+            import warnings
+            try:
+                # XXX!  The goal is to ensure that the warnings.warn()
+                # will not suppress the warning.  We want to get it
+                # several times if we reach this point several times.
+                __warningregistry__.clear()
+            except NameError:
+                pass
+            warnings.warn("%r has no values explicitly defined; "
+                          "guessing that it is equivalent to 'unsigned int'"
+                          % self._get_c_name())
+            smallest_value = largest_value = 0
+        if smallest_value < 0:   # needs a signed type
+            sign = 1
+            candidate1 = PrimitiveType("int")
+            candidate2 = PrimitiveType("long")
+        else:
+            sign = 0
+            candidate1 = PrimitiveType("unsigned int")
+            candidate2 = PrimitiveType("unsigned long")
+        btype1 = candidate1.get_cached_btype(ffi, finishlist)
+        btype2 = candidate2.get_cached_btype(ffi, finishlist)
+        size1 = ffi.sizeof(btype1)
+        size2 = ffi.sizeof(btype2)
+        if (smallest_value >= ((-1) << (8*size1-1)) and
+            largest_value < (1 << (8*size1-sign))):
+            return btype1
+        if (smallest_value >= ((-1) << (8*size2-1)) and
+            largest_value < (1 << (8*size2-sign))):
+            return btype2
+        raise CDefError("%s values don't all fit into either 'long' "
+                        "or 'unsigned long'" % self._get_c_name())
+
+def unknown_type(name, structname=None):
+    if structname is None:
+        structname = '$%s' % name
+    tp = StructType(structname, None, None, None)
+    tp.force_the_name(name)
+    tp.origin = "unknown_type"
+    return tp
+
+def unknown_ptr_type(name, structname=None):
+    if structname is None:
+        structname = '$$%s' % name
+    tp = StructType(structname, None, None, None)
+    return NamedPointerType(tp, name)
+
+
+global_lock = allocate_lock()
+_typecache_cffi_backend = weakref.WeakValueDictionary()
+
+def get_typecache(backend):
+    # returns _typecache_cffi_backend if backend is the _cffi_backend
+    # module, or type(backend).__typecache if backend is an instance of
+    # CTypesBackend (or some FakeBackend class during tests)
+    if isinstance(backend, types.ModuleType):
+        return _typecache_cffi_backend
+    with global_lock:
+        if not hasattr(type(backend), '__typecache'):
+            type(backend).__typecache = weakref.WeakValueDictionary()
+        return type(backend).__typecache
+
+def global_cache(srctype, ffi, funcname, *args, **kwds):
+    key = kwds.pop('key', (funcname, args))
+    assert not kwds
+    try:
+        return ffi._typecache[key]
+    except KeyError:
+        pass
+    try:
+        res = getattr(ffi._backend, funcname)(*args)
+    except NotImplementedError as e:
+        raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e))
+    # note that setdefault() on WeakValueDictionary is not atomic
+    # and contains a rare bug (http://bugs.python.org/issue19542);
+    # we have to use a lock and do it ourselves
+    cache = ffi._typecache
+    with global_lock:
+        res1 = cache.get(key)
+        if res1 is None:
+            cache[key] = res
+            return res
+        else:
+            return res1
+
+def pointer_cache(ffi, BType):
+    return global_cache('?', ffi, 'new_pointer_type', BType)
+
+def attach_exception_info(e, name):
+    if e.args and type(e.args[0]) is str:
+        e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]
diff --git a/.venv/lib/python3.8/site-packages/cffi/parse_c_type.h b/.venv/lib/python3.8/site-packages/cffi/parse_c_type.h
new file mode 100644
index 0000000000000000000000000000000000000000..84e4ef85659eb63e6453d8af9f024f1866182342
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/parse_c_type.h
@@ -0,0 +1,181 @@
+
+/* This part is from file 'cffi/parse_c_type.h'.  It is copied at the
+   beginning of C sources generated by CFFI's ffi.set_source(). */
+
+typedef void *_cffi_opcode_t;
+
+#define _CFFI_OP(opcode, arg)   (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8))
+#define _CFFI_GETOP(cffi_opcode)    ((unsigned char)(uintptr_t)cffi_opcode)
+#define _CFFI_GETARG(cffi_opcode)   (((intptr_t)cffi_opcode) >> 8)
+
+#define _CFFI_OP_PRIMITIVE       1
+#define _CFFI_OP_POINTER         3
+#define _CFFI_OP_ARRAY           5
+#define _CFFI_OP_OPEN_ARRAY      7
+#define _CFFI_OP_STRUCT_UNION    9
+#define _CFFI_OP_ENUM           11
+#define _CFFI_OP_FUNCTION       13
+#define _CFFI_OP_FUNCTION_END   15
+#define _CFFI_OP_NOOP           17
+#define _CFFI_OP_BITFIELD       19
+#define _CFFI_OP_TYPENAME       21
+#define _CFFI_OP_CPYTHON_BLTN_V 23   // varargs
+#define _CFFI_OP_CPYTHON_BLTN_N 25   // noargs
+#define _CFFI_OP_CPYTHON_BLTN_O 27   // O  (i.e. a single arg)
+#define _CFFI_OP_CONSTANT       29
+#define _CFFI_OP_CONSTANT_INT   31
+#define _CFFI_OP_GLOBAL_VAR     33
+#define _CFFI_OP_DLOPEN_FUNC    35
+#define _CFFI_OP_DLOPEN_CONST   37
+#define _CFFI_OP_GLOBAL_VAR_F   39
+#define _CFFI_OP_EXTERN_PYTHON  41
+
+#define _CFFI_PRIM_VOID          0
+#define _CFFI_PRIM_BOOL          1
+#define _CFFI_PRIM_CHAR          2
+#define _CFFI_PRIM_SCHAR         3
+#define _CFFI_PRIM_UCHAR         4
+#define _CFFI_PRIM_SHORT         5
+#define _CFFI_PRIM_USHORT        6
+#define _CFFI_PRIM_INT           7
+#define _CFFI_PRIM_UINT          8
+#define _CFFI_PRIM_LONG          9
+#define _CFFI_PRIM_ULONG        10
+#define _CFFI_PRIM_LONGLONG     11
+#define _CFFI_PRIM_ULONGLONG    12
+#define _CFFI_PRIM_FLOAT        13
+#define _CFFI_PRIM_DOUBLE       14
+#define _CFFI_PRIM_LONGDOUBLE   15
+
+#define _CFFI_PRIM_WCHAR        16
+#define _CFFI_PRIM_INT8         17
+#define _CFFI_PRIM_UINT8        18
+#define _CFFI_PRIM_INT16        19
+#define _CFFI_PRIM_UINT16       20
+#define _CFFI_PRIM_INT32        21
+#define _CFFI_PRIM_UINT32       22
+#define _CFFI_PRIM_INT64        23
+#define _CFFI_PRIM_UINT64       24
+#define _CFFI_PRIM_INTPTR       25
+#define _CFFI_PRIM_UINTPTR      26
+#define _CFFI_PRIM_PTRDIFF      27
+#define _CFFI_PRIM_SIZE         28
+#define _CFFI_PRIM_SSIZE        29
+#define _CFFI_PRIM_INT_LEAST8   30
+#define _CFFI_PRIM_UINT_LEAST8  31
+#define _CFFI_PRIM_INT_LEAST16  32
+#define _CFFI_PRIM_UINT_LEAST16 33
+#define _CFFI_PRIM_INT_LEAST32  34
+#define _CFFI_PRIM_UINT_LEAST32 35
+#define _CFFI_PRIM_INT_LEAST64  36
+#define _CFFI_PRIM_UINT_LEAST64 37
+#define _CFFI_PRIM_INT_FAST8    38
+#define _CFFI_PRIM_UINT_FAST8   39
+#define _CFFI_PRIM_INT_FAST16   40
+#define _CFFI_PRIM_UINT_FAST16  41
+#define _CFFI_PRIM_INT_FAST32   42
+#define _CFFI_PRIM_UINT_FAST32  43
+#define _CFFI_PRIM_INT_FAST64   44
+#define _CFFI_PRIM_UINT_FAST64  45
+#define _CFFI_PRIM_INTMAX       46
+#define _CFFI_PRIM_UINTMAX      47
+#define _CFFI_PRIM_FLOATCOMPLEX 48
+#define _CFFI_PRIM_DOUBLECOMPLEX 49
+#define _CFFI_PRIM_CHAR16       50
+#define _CFFI_PRIM_CHAR32       51
+
+#define _CFFI__NUM_PRIM         52
+#define _CFFI__UNKNOWN_PRIM           (-1)
+#define _CFFI__UNKNOWN_FLOAT_PRIM     (-2)
+#define _CFFI__UNKNOWN_LONG_DOUBLE    (-3)
+
+#define _CFFI__IO_FILE_STRUCT         (-1)
+
+
+struct _cffi_global_s {
+    const char *name;
+    void *address;
+    _cffi_opcode_t type_op;
+    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
+                              // OP_CPYTHON_BLTN_*: addr of direct function
+};
+
+struct _cffi_getconst_s {
+    unsigned long long value;
+    const struct _cffi_type_context_s *ctx;
+    int gindex;
+};
+
+struct _cffi_struct_union_s {
+    const char *name;
+    int type_index;          // -> _cffi_types, on a OP_STRUCT_UNION
+    int flags;               // _CFFI_F_* flags below
+    size_t size;
+    int alignment;
+    int first_field_index;   // -> _cffi_fields array
+    int num_fields;
+};
+#define _CFFI_F_UNION         0x01   // is a union, not a struct
+#define _CFFI_F_CHECK_FIELDS  0x02   // complain if fields are not in the
+                                     // "standard layout" or if some are missing
+#define _CFFI_F_PACKED        0x04   // for CHECK_FIELDS, assume a packed struct
+#define _CFFI_F_EXTERNAL      0x08   // in some other ffi.include()
+#define _CFFI_F_OPAQUE        0x10   // opaque
+
+struct _cffi_field_s {
+    const char *name;
+    size_t field_offset;
+    size_t field_size;
+    _cffi_opcode_t field_type_op;
+};
+
+struct _cffi_enum_s {
+    const char *name;
+    int type_index;          // -> _cffi_types, on a OP_ENUM
+    int type_prim;           // _CFFI_PRIM_xxx
+    const char *enumerators; // comma-delimited string
+};
+
+struct _cffi_typename_s {
+    const char *name;
+    int type_index;   /* if opaque, points to a possibly artificial
+                         OP_STRUCT which is itself opaque */
+};
+
+struct _cffi_type_context_s {
+    _cffi_opcode_t *types;
+    const struct _cffi_global_s *globals;
+    const struct _cffi_field_s *fields;
+    const struct _cffi_struct_union_s *struct_unions;
+    const struct _cffi_enum_s *enums;
+    const struct _cffi_typename_s *typenames;
+    int num_globals;
+    int num_struct_unions;
+    int num_enums;
+    int num_typenames;
+    const char *const *includes;
+    int num_types;
+    int flags;      /* future extension */
+};
+
+struct _cffi_parse_info_s {
+    const struct _cffi_type_context_s *ctx;
+    _cffi_opcode_t *output;
+    unsigned int output_size;
+    size_t error_location;
+    const char *error_message;
+};
+
+struct _cffi_externpy_s {
+    const char *name;
+    size_t size_of_result;
+    void *reserved1, *reserved2;
+};
+
+#ifdef _CFFI_INTERNAL
+static int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
+static int search_in_globals(const struct _cffi_type_context_s *ctx,
+                             const char *search, size_t search_len);
+static int search_in_struct_unions(const struct _cffi_type_context_s *ctx,
+                                   const char *search, size_t search_len);
+#endif
diff --git a/.venv/lib/python3.8/site-packages/cffi/pkgconfig.py b/.venv/lib/python3.8/site-packages/cffi/pkgconfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c93f15a60e6f904b2dd108d6e22044a5890bcb4
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/pkgconfig.py
@@ -0,0 +1,121 @@
+# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi
+import sys, os, subprocess
+
+from .error import PkgConfigError
+
+
+def merge_flags(cfg1, cfg2):
+    """Merge values from cffi config flags cfg2 to cf1
+
+    Example:
+        merge_flags({"libraries": ["one"]}, {"libraries": ["two"]})
+        {"libraries": ["one", "two"]}
+    """
+    for key, value in cfg2.items():
+        if key not in cfg1:
+            cfg1[key] = value
+        else:
+            if not isinstance(cfg1[key], list):
+                raise TypeError("cfg1[%r] should be a list of strings" % (key,))
+            if not isinstance(value, list):
+                raise TypeError("cfg2[%r] should be a list of strings" % (key,))
+            cfg1[key].extend(value)
+    return cfg1
+
+
+def call(libname, flag, encoding=sys.getfilesystemencoding()):
+    """Calls pkg-config and returns the output if found
+    """
+    a = ["pkg-config", "--print-errors"]
+    a.append(flag)
+    a.append(libname)
+    try:
+        pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    except EnvironmentError as e:
+        raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),))
+
+    bout, berr = pc.communicate()
+    if pc.returncode != 0:
+        try:
+            berr = berr.decode(encoding)
+        except Exception:
+            pass
+        raise PkgConfigError(berr.strip())
+
+    if sys.version_info >= (3,) and not isinstance(bout, str):   # Python 3.x
+        try:
+            bout = bout.decode(encoding)
+        except UnicodeDecodeError:
+            raise PkgConfigError("pkg-config %s %s returned bytes that cannot "
+                                 "be decoded with encoding %r:\n%r" %
+                                 (flag, libname, encoding, bout))
+
+    if os.altsep != '\\' and '\\' in bout:
+        raise PkgConfigError("pkg-config %s %s returned an unsupported "
+                             "backslash-escaped output:\n%r" %
+                             (flag, libname, bout))
+    return bout
+
+
+def flags_from_pkgconfig(libs):
+    r"""Return compiler line flags for FFI.set_source based on pkg-config output
+
+    Usage
+        ...
+        ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"])
+
+    If pkg-config is installed on build machine, then arguments include_dirs,
+    library_dirs, libraries, define_macros, extra_compile_args and
+    extra_link_args are extended with an output of pkg-config for libfoo and
+    libbar.
+
+    Raises PkgConfigError in case the pkg-config call fails.
+    """
+
+    def get_include_dirs(string):
+        return [x[2:] for x in string.split() if x.startswith("-I")]
+
+    def get_library_dirs(string):
+        return [x[2:] for x in string.split() if x.startswith("-L")]
+
+    def get_libraries(string):
+        return [x[2:] for x in string.split() if x.startswith("-l")]
+
+    # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils
+    def get_macros(string):
+        def _macro(x):
+            x = x[2:]    # drop "-D"
+            if '=' in x:
+                return tuple(x.split("=", 1))  # "-Dfoo=bar" => ("foo", "bar")
+            else:
+                return (x, None)               # "-Dfoo" => ("foo", None)
+        return [_macro(x) for x in string.split() if x.startswith("-D")]
+
+    def get_other_cflags(string):
+        return [x for x in string.split() if not x.startswith("-I") and
+                                             not x.startswith("-D")]
+
+    def get_other_libs(string):
+        return [x for x in string.split() if not x.startswith("-L") and
+                                             not x.startswith("-l")]
+
+    # return kwargs for given libname
+    def kwargs(libname):
+        fse = sys.getfilesystemencoding()
+        all_cflags = call(libname, "--cflags")
+        all_libs = call(libname, "--libs")
+        return {
+            "include_dirs": get_include_dirs(all_cflags),
+            "library_dirs": get_library_dirs(all_libs),
+            "libraries": get_libraries(all_libs),
+            "define_macros": get_macros(all_cflags),
+            "extra_compile_args": get_other_cflags(all_cflags),
+            "extra_link_args": get_other_libs(all_libs),
+            }
+
+    # merge all arguments together
+    ret = {}
+    for libname in libs:
+        lib_flags = kwargs(libname)
+        merge_flags(ret, lib_flags)
+    return ret
diff --git a/.venv/lib/python3.8/site-packages/cffi/recompiler.py b/.venv/lib/python3.8/site-packages/cffi/recompiler.py
new file mode 100644
index 0000000000000000000000000000000000000000..57781a3cad616a7adf82dc7f0c701167467e3dd2
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/recompiler.py
@@ -0,0 +1,1598 @@
+import os, sys, io
+from . import ffiplatform, model
+from .error import VerificationError
+from .cffi_opcode import *
+
+VERSION_BASE = 0x2601
+VERSION_EMBEDDED = 0x2701
+VERSION_CHAR16CHAR32 = 0x2801
+
+USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or
+                   sys.version_info >= (3, 5))
+
+
+class GlobalExpr:
+    def __init__(self, name, address, type_op, size=0, check_value=0):
+        self.name = name
+        self.address = address
+        self.type_op = type_op
+        self.size = size
+        self.check_value = check_value
+
+    def as_c_expr(self):
+        return '  { "%s", (void *)%s, %s, (void *)%s },' % (
+            self.name, self.address, self.type_op.as_c_expr(), self.size)
+
+    def as_python_expr(self):
+        return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
+                               self.check_value)
+
+class FieldExpr:
+    def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):
+        self.name = name
+        self.field_offset = field_offset
+        self.field_size = field_size
+        self.fbitsize = fbitsize
+        self.field_type_op = field_type_op
+
+    def as_c_expr(self):
+        spaces = " " * len(self.name)
+        return ('  { "%s", %s,\n' % (self.name, self.field_offset) +
+                '     %s   %s,\n' % (spaces, self.field_size) +
+                '     %s   %s },' % (spaces, self.field_type_op.as_c_expr()))
+
+    def as_python_expr(self):
+        raise NotImplementedError
+
+    def as_field_python_expr(self):
+        if self.field_type_op.op == OP_NOOP:
+            size_expr = ''
+        elif self.field_type_op.op == OP_BITFIELD:
+            size_expr = format_four_bytes(self.fbitsize)
+        else:
+            raise NotImplementedError
+        return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),
+                              size_expr,
+                              self.name)
+
+class StructUnionExpr:
+    def __init__(self, name, type_index, flags, size, alignment, comment,
+                 first_field_index, c_fields):
+        self.name = name
+        self.type_index = type_index
+        self.flags = flags
+        self.size = size
+        self.alignment = alignment
+        self.comment = comment
+        self.first_field_index = first_field_index
+        self.c_fields = c_fields
+
+    def as_c_expr(self):
+        return ('  { "%s", %d, %s,' % (self.name, self.type_index, self.flags)
+                + '\n    %s, %s, ' % (self.size, self.alignment)
+                + '%d, %d ' % (self.first_field_index, len(self.c_fields))
+                + ('/* %s */ ' % self.comment if self.comment else '')
+                + '},')
+
+    def as_python_expr(self):
+        flags = eval(self.flags, G_FLAGS)
+        fields_expr = [c_field.as_field_python_expr()
+                       for c_field in self.c_fields]
+        return "(b'%s%s%s',%s)" % (
+            format_four_bytes(self.type_index),
+            format_four_bytes(flags),
+            self.name,
+            ','.join(fields_expr))
+
+class EnumExpr:
+    def __init__(self, name, type_index, size, signed, allenums):
+        self.name = name
+        self.type_index = type_index
+        self.size = size
+        self.signed = signed
+        self.allenums = allenums
+
+    def as_c_expr(self):
+        return ('  { "%s", %d, _cffi_prim_int(%s, %s),\n'
+                '    "%s" },' % (self.name, self.type_index,
+                                 self.size, self.signed, self.allenums))
+
+    def as_python_expr(self):
+        prim_index = {
+            (1, 0): PRIM_UINT8,  (1, 1):  PRIM_INT8,
+            (2, 0): PRIM_UINT16, (2, 1):  PRIM_INT16,
+            (4, 0): PRIM_UINT32, (4, 1):  PRIM_INT32,
+            (8, 0): PRIM_UINT64, (8, 1):  PRIM_INT64,
+            }[self.size, self.signed]
+        return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),
+                                     format_four_bytes(prim_index),
+                                     self.name, self.allenums)
+
+class TypenameExpr:
+    def __init__(self, name, type_index):
+        self.name = name
+        self.type_index = type_index
+
+    def as_c_expr(self):
+        return '  { "%s", %d },' % (self.name, self.type_index)
+
+    def as_python_expr(self):
+        return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
+
+
+# ____________________________________________________________
+
+
+class Recompiler:
+    _num_externpy = 0
+
+    def __init__(self, ffi, module_name, target_is_python=False):
+        self.ffi = ffi
+        self.module_name = module_name
+        self.target_is_python = target_is_python
+        self._version = VERSION_BASE
+
+    def needs_version(self, ver):
+        self._version = max(self._version, ver)
+
+    def collect_type_table(self):
+        self._typesdict = {}
+        self._generate("collecttype")
+        #
+        all_decls = sorted(self._typesdict, key=str)
+        #
+        # prepare all FUNCTION bytecode sequences first
+        self.cffi_types = []
+        for tp in all_decls:
+            if tp.is_raw_function:
+                assert self._typesdict[tp] is None
+                self._typesdict[tp] = len(self.cffi_types)
+                self.cffi_types.append(tp)     # placeholder
+                for tp1 in tp.args:
+                    assert isinstance(tp1, (model.VoidType,
+                                            model.BasePrimitiveType,
+                                            model.PointerType,
+                                            model.StructOrUnionOrEnum,
+                                            model.FunctionPtrType))
+                    if self._typesdict[tp1] is None:
+                        self._typesdict[tp1] = len(self.cffi_types)
+                    self.cffi_types.append(tp1)   # placeholder
+                self.cffi_types.append('END')     # placeholder
+        #
+        # prepare all OTHER bytecode sequences
+        for tp in all_decls:
+            if not tp.is_raw_function and self._typesdict[tp] is None:
+                self._typesdict[tp] = len(self.cffi_types)
+                self.cffi_types.append(tp)        # placeholder
+                if tp.is_array_type and tp.length is not None:
+                    self.cffi_types.append('LEN') # placeholder
+        assert None not in self._typesdict.values()
+        #
+        # collect all structs and unions and enums
+        self._struct_unions = {}
+        self._enums = {}
+        for tp in all_decls:
+            if isinstance(tp, model.StructOrUnion):
+                self._struct_unions[tp] = None
+            elif isinstance(tp, model.EnumType):
+                self._enums[tp] = None
+        for i, tp in enumerate(sorted(self._struct_unions,
+                                      key=lambda tp: tp.name)):
+            self._struct_unions[tp] = i
+        for i, tp in enumerate(sorted(self._enums,
+                                      key=lambda tp: tp.name)):
+            self._enums[tp] = i
+        #
+        # emit all bytecode sequences now
+        for tp in all_decls:
+            method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)
+            method(tp, self._typesdict[tp])
+        #
+        # consistency check
+        for op in self.cffi_types:
+            assert isinstance(op, CffiOp)
+        self.cffi_types = tuple(self.cffi_types)    # don't change any more
+
+    def _enum_fields(self, tp):
+        # When producing C, expand all anonymous struct/union fields.
+        # That's necessary to have C code checking the offsets of the
+        # individual fields contained in them.  When producing Python,
+        # don't do it and instead write it like it is, with the
+        # corresponding fields having an empty name.  Empty names are
+        # recognized at runtime when we import the generated Python
+        # file.
+        expand_anonymous_struct_union = not self.target_is_python
+        return tp.enumfields(expand_anonymous_struct_union)
+
+    def _do_collect_type(self, tp):
+        if not isinstance(tp, model.BaseTypeByIdentity):
+            if isinstance(tp, tuple):
+                for x in tp:
+                    self._do_collect_type(x)
+            return
+        if tp not in self._typesdict:
+            self._typesdict[tp] = None
+            if isinstance(tp, model.FunctionPtrType):
+                self._do_collect_type(tp.as_raw_function())
+            elif isinstance(tp, model.StructOrUnion):
+                if tp.fldtypes is not None and (
+                        tp not in self.ffi._parser._included_declarations):
+                    for name1, tp1, _, _ in self._enum_fields(tp):
+                        self._do_collect_type(self._field_type(tp, name1, tp1))
+            else:
+                for _, x in tp._get_items():
+                    self._do_collect_type(x)
+
+    def _generate(self, step_name):
+        lst = self.ffi._parser._declarations.items()
+        for name, (tp, quals) in sorted(lst):
+            kind, realname = name.split(' ', 1)
+            try:
+                method = getattr(self, '_generate_cpy_%s_%s' % (kind,
+                                                                step_name))
+            except AttributeError:
+                raise VerificationError(
+                    "not implemented in recompile(): %r" % name)
+            try:
+                self._current_quals = quals
+                method(tp, realname)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    # ----------
+
+    ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
+
+    def collect_step_tables(self):
+        # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
+        self._lsts = {}
+        for step_name in self.ALL_STEPS:
+            self._lsts[step_name] = []
+        self._seen_struct_unions = set()
+        self._generate("ctx")
+        self._add_missing_struct_unions()
+        #
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            if step_name != "field":
+                lst.sort(key=lambda entry: entry.name)
+            self._lsts[step_name] = tuple(lst)    # don't change any more
+        #
+        # check for a possible internal inconsistency: _cffi_struct_unions
+        # should have been generated with exactly self._struct_unions
+        lst = self._lsts["struct_union"]
+        for tp, i in self._struct_unions.items():
+            assert i < len(lst)
+            assert lst[i].name == tp.name
+        assert len(lst) == len(self._struct_unions)
+        # same with enums
+        lst = self._lsts["enum"]
+        for tp, i in self._enums.items():
+            assert i < len(lst)
+            assert lst[i].name == tp.name
+        assert len(lst) == len(self._enums)
+
+    # ----------
+
+    def _prnt(self, what=''):
+        self._f.write(what + '\n')
+
+    def write_source_to_f(self, f, preamble):
+        if self.target_is_python:
+            assert preamble is None
+            self.write_py_source_to_f(f)
+        else:
+            assert preamble is not None
+            self.write_c_source_to_f(f, preamble)
+
+    def _rel_readlines(self, filename):
+        g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
+        lines = g.readlines()
+        g.close()
+        return lines
+
+    def write_c_source_to_f(self, f, preamble):
+        self._f = f
+        prnt = self._prnt
+        if self.ffi._embedding is not None:
+            prnt('#define _CFFI_USE_EMBEDDING')
+        if not USE_LIMITED_API:
+            prnt('#define _CFFI_NO_LIMITED_API')
+        #
+        # first the '#include' (actually done by inlining the file's content)
+        lines = self._rel_readlines('_cffi_include.h')
+        i = lines.index('#include "parse_c_type.h"\n')
+        lines[i:i+1] = self._rel_readlines('parse_c_type.h')
+        prnt(''.join(lines))
+        #
+        # if we have ffi._embedding != None, we give it here as a macro
+        # and include an extra file
+        base_module_name = self.module_name.split('.')[-1]
+        if self.ffi._embedding is not None:
+            prnt('#define _CFFI_MODULE_NAME  "%s"' % (self.module_name,))
+            prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')
+            self._print_string_literal_in_array(self.ffi._embedding)
+            prnt('0 };')
+            prnt('#ifdef PYPY_VERSION')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  _cffi_pypyinit_%s' % (
+                base_module_name,))
+            prnt('#elif PY_MAJOR_VERSION >= 3')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  PyInit_%s' % (
+                base_module_name,))
+            prnt('#else')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  init%s' % (
+                base_module_name,))
+            prnt('#endif')
+            lines = self._rel_readlines('_embedding.h')
+            i = lines.index('#include "_cffi_errors.h"\n')
+            lines[i:i+1] = self._rel_readlines('_cffi_errors.h')
+            prnt(''.join(lines))
+            self.needs_version(VERSION_EMBEDDED)
+        #
+        # then paste the C source given by the user, verbatim.
+        prnt('/************************************************************/')
+        prnt()
+        prnt(preamble)
+        prnt()
+        prnt('/************************************************************/')
+        prnt()
+        #
+        # the declaration of '_cffi_types'
+        prnt('static void *_cffi_types[] = {')
+        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
+        for i, op in enumerate(self.cffi_types):
+            comment = ''
+            if i in typeindex2type:
+                comment = ' // ' + typeindex2type[i]._get_c_name()
+            prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))
+        if not self.cffi_types:
+            prnt('  0')
+        prnt('};')
+        prnt()
+        #
+        # call generate_cpy_xxx_decl(), for every xxx found from
+        # ffi._parser._declarations.  This generates all the functions.
+        self._seen_constants = set()
+        self._generate("decl")
+        #
+        # the declaration of '_cffi_globals' and '_cffi_typenames'
+        nums = {}
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            nums[step_name] = len(lst)
+            if nums[step_name] > 0:
+                prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
+                    step_name, step_name))
+                for entry in lst:
+                    prnt(entry.as_c_expr())
+                prnt('};')
+                prnt()
+        #
+        # the declaration of '_cffi_includes'
+        if self.ffi._included_ffis:
+            prnt('static const char * const _cffi_includes[] = {')
+            for ffi_to_include in self.ffi._included_ffis:
+                try:
+                    included_module_name, included_source = (
+                        ffi_to_include._assigned_source[:2])
+                except AttributeError:
+                    raise VerificationError(
+                        "ffi object %r includes %r, but the latter has not "
+                        "been prepared with set_source()" % (
+                            self.ffi, ffi_to_include,))
+                if included_source is None:
+                    raise VerificationError(
+                        "not implemented yet: ffi.include() of a Python-based "
+                        "ffi inside a C-based ffi")
+                prnt('  "%s",' % (included_module_name,))
+            prnt('  NULL')
+            prnt('};')
+            prnt()
+        #
+        # the declaration of '_cffi_type_context'
+        prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
+        prnt('  _cffi_types,')
+        for step_name in self.ALL_STEPS:
+            if nums[step_name] > 0:
+                prnt('  _cffi_%ss,' % step_name)
+            else:
+                prnt('  NULL,  /* no %ss */' % step_name)
+        for step_name in self.ALL_STEPS:
+            if step_name != "field":
+                prnt('  %d,  /* num_%ss */' % (nums[step_name], step_name))
+        if self.ffi._included_ffis:
+            prnt('  _cffi_includes,')
+        else:
+            prnt('  NULL,  /* no includes */')
+        prnt('  %d,  /* num_types */' % (len(self.cffi_types),))
+        flags = 0
+        if self._num_externpy > 0 or self.ffi._embedding is not None:
+            flags |= 1     # set to mean that we use extern "Python"
+        prnt('  %d,  /* flags */' % flags)
+        prnt('};')
+        prnt()
+        #
+        # the init function
+        prnt('#ifdef __GNUC__')
+        prnt('#  pragma GCC visibility push(default)  /* for -fvisibility= */')
+        prnt('#endif')
+        prnt()
+        prnt('#ifdef PYPY_VERSION')
+        prnt('PyMODINIT_FUNC')
+        prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
+        prnt('{')
+        if flags & 1:
+            prnt('    if (((intptr_t)p[0]) >= 0x0A03) {')
+            prnt('        _cffi_call_python_org = '
+                 '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
+            prnt('    }')
+        prnt('    p[0] = (const void *)0x%x;' % self._version)
+        prnt('    p[1] = &_cffi_type_context;')
+        prnt('#if PY_MAJOR_VERSION >= 3')
+        prnt('    return NULL;')
+        prnt('#endif')
+        prnt('}')
+        # on Windows, distutils insists on putting init_cffi_xyz in
+        # 'export_symbols', so instead of fighting it, just give up and
+        # give it one
+        prnt('#  ifdef _MSC_VER')
+        prnt('     PyMODINIT_FUNC')
+        prnt('#  if PY_MAJOR_VERSION >= 3')
+        prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))
+        prnt('#  else')
+        prnt('     init%s(void) { }' % (base_module_name,))
+        prnt('#  endif')
+        prnt('#  endif')
+        prnt('#elif PY_MAJOR_VERSION >= 3')
+        prnt('PyMODINIT_FUNC')
+        prnt('PyInit_%s(void)' % (base_module_name,))
+        prnt('{')
+        prnt('  return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
+            self.module_name, self._version))
+        prnt('}')
+        prnt('#else')
+        prnt('PyMODINIT_FUNC')
+        prnt('init%s(void)' % (base_module_name,))
+        prnt('{')
+        prnt('  _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
+            self.module_name, self._version))
+        prnt('}')
+        prnt('#endif')
+        prnt()
+        prnt('#ifdef __GNUC__')
+        prnt('#  pragma GCC visibility pop')
+        prnt('#endif')
+        self._version = None
+
+    def _to_py(self, x):
+        if isinstance(x, str):
+            return "b'%s'" % (x,)
+        if isinstance(x, (list, tuple)):
+            rep = [self._to_py(item) for item in x]
+            if len(rep) == 1:
+                rep.append('')
+            return "(%s)" % (','.join(rep),)
+        return x.as_python_expr()  # Py2: unicode unexpected; Py3: bytes unexp.
+
+    def write_py_source_to_f(self, f):
+        self._f = f
+        prnt = self._prnt
+        #
+        # header
+        prnt("# auto-generated file")
+        prnt("import _cffi_backend")
+        #
+        # the 'import' of the included ffis
+        num_includes = len(self.ffi._included_ffis or ())
+        for i in range(num_includes):
+            ffi_to_include = self.ffi._included_ffis[i]
+            try:
+                included_module_name, included_source = (
+                    ffi_to_include._assigned_source[:2])
+            except AttributeError:
+                raise VerificationError(
+                    "ffi object %r includes %r, but the latter has not "
+                    "been prepared with set_source()" % (
+                        self.ffi, ffi_to_include,))
+            if included_source is not None:
+                raise VerificationError(
+                    "not implemented yet: ffi.include() of a C-based "
+                    "ffi inside a Python-based ffi")
+            prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
+        prnt()
+        prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
+        prnt("    _version = 0x%x," % (self._version,))
+        self._version = None
+        #
+        # the '_types' keyword argument
+        self.cffi_types = tuple(self.cffi_types)    # don't change any more
+        types_lst = [op.as_python_bytes() for op in self.cffi_types]
+        prnt('    _types = %s,' % (self._to_py(''.join(types_lst)),))
+        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
+        #
+        # the keyword arguments from ALL_STEPS
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            if len(lst) > 0 and step_name != "field":
+                prnt('    _%ss = %s,' % (step_name, self._to_py(lst)))
+        #
+        # the '_includes' keyword argument
+        if num_includes > 0:
+            prnt('    _includes = (%s,),' % (
+                ', '.join(['_ffi%d' % i for i in range(num_includes)]),))
+        #
+        # the footer
+        prnt(')')
+
+    # ----------
+
+    def _gettypenum(self, type):
+        # a KeyError here is a bug.  please report it! :-)
+        return self._typesdict[type]
+
+    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
+        extraarg = ''
+        if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
+            if tp.is_integer_type() and tp.name != '_Bool':
+                converter = '_cffi_to_c_int'
+                extraarg = ', %s' % tp.name
+            elif isinstance(tp, model.UnknownFloatType):
+                # don't check with is_float_type(): it may be a 'long
+                # double' here, and _cffi_to_c_double would loose precision
+                converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
+            else:
+                cname = tp.get_c_name('')
+                converter = '(%s)_cffi_to_c_%s' % (cname,
+                                                   tp.name.replace(' ', '_'))
+                if cname in ('char16_t', 'char32_t'):
+                    self.needs_version(VERSION_CHAR16CHAR32)
+            errvalue = '-1'
+        #
+        elif isinstance(tp, model.PointerType):
+            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
+                                                    tovar, errcode)
+            return
+        #
+        elif (isinstance(tp, model.StructOrUnionOrEnum) or
+              isinstance(tp, model.BasePrimitiveType)):
+            # a struct (not a struct pointer) as a function argument;
+            # or, a complex (the same code works)
+            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
+                      % (tovar, self._gettypenum(tp), fromvar))
+            self._prnt('    %s;' % errcode)
+            return
+        #
+        elif isinstance(tp, model.FunctionPtrType):
+            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+            errvalue = 'NULL'
+        #
+        else:
+            raise NotImplementedError(tp)
+        #
+        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
+        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
+            tovar, tp.get_c_name(''), errvalue))
+        self._prnt('    %s;' % errcode)
+
+    def _extra_local_variables(self, tp, localvars, freelines):
+        if isinstance(tp, model.PointerType):
+            localvars.add('Py_ssize_t datasize')
+            localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
+            freelines.add('if (large_args_free != NULL)'
+                          ' _cffi_free_array_arguments(large_args_free);')
+
+    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
+        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
+        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
+            self._gettypenum(tp), fromvar, tovar))
+        self._prnt('  if (datasize != 0) {')
+        self._prnt('    %s = ((size_t)datasize) <= 640 ? '
+                   '(%s)alloca((size_t)datasize) : NULL;' % (
+            tovar, tp.get_c_name('')))
+        self._prnt('    if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
+                   '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
+        self._prnt('            datasize, &large_args_free) < 0)')
+        self._prnt('      %s;' % errcode)
+        self._prnt('  }')
+
+    def _convert_expr_from_c(self, tp, var, context):
+        if isinstance(tp, model.BasePrimitiveType):
+            if tp.is_integer_type() and tp.name != '_Bool':
+                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
+            elif isinstance(tp, model.UnknownFloatType):
+                return '_cffi_from_c_double(%s)' % (var,)
+            elif tp.name != 'long double' and not tp.is_complex_type():
+                cname = tp.name.replace(' ', '_')
+                if cname in ('char16_t', 'char32_t'):
+                    self.needs_version(VERSION_CHAR16CHAR32)
+                return '_cffi_from_c_%s(%s)' % (cname, var)
+            else:
+                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                    var, self._gettypenum(tp))
+        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.ArrayType):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(model.PointerType(tp.item)))
+        elif isinstance(tp, model.StructOrUnion):
+            if tp.fldnames is None:
+                raise TypeError("'%s' is used as %s, but is opaque" % (
+                    tp._get_c_name(), context))
+            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.EnumType):
+            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        else:
+            raise NotImplementedError(tp)
+
+    # ----------
+    # typedefs
+
+    def _typedef_type(self, tp, name):
+        return self._global_type(tp, "(*(%s *)0)" % (name,))
+
+    def _generate_cpy_typedef_collecttype(self, tp, name):
+        self._do_collect_type(self._typedef_type(tp, name))
+
+    def _generate_cpy_typedef_decl(self, tp, name):
+        pass
+
+    def _typedef_ctx(self, tp, name):
+        type_index = self._typesdict[tp]
+        self._lsts["typename"].append(TypenameExpr(name, type_index))
+
+    def _generate_cpy_typedef_ctx(self, tp, name):
+        tp = self._typedef_type(tp, name)
+        self._typedef_ctx(tp, name)
+        if getattr(tp, "origin", None) == "unknown_type":
+            self._struct_ctx(tp, tp.name, approxname=None)
+        elif isinstance(tp, model.NamedPointerType):
+            self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,
+                             named_ptr=tp)
+
+    # ----------
+    # function declarations
+
+    def _generate_cpy_function_collecttype(self, tp, name):
+        self._do_collect_type(tp.as_raw_function())
+        if tp.ellipsis and not self.target_is_python:
+            self._do_collect_type(tp)
+
+    def _generate_cpy_function_decl(self, tp, name):
+        assert not self.target_is_python
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            # cannot support vararg functions better than this: check for its
+            # exact type (including the fixed arguments), and build it as a
+            # constant function pointer (no CPython wrapper)
+            self._generate_cpy_constant_decl(tp, name)
+            return
+        prnt = self._prnt
+        numargs = len(tp.args)
+        if numargs == 0:
+            argname = 'noarg'
+        elif numargs == 1:
+            argname = 'arg0'
+        else:
+            argname = 'args'
+        #
+        # ------------------------------
+        # the 'd' version of the function, only for addressof(lib, 'func')
+        arguments = []
+        call_arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arguments.append(type.get_c_name(' x%d' % i, context))
+            call_arguments.append('x%d' % i)
+        repr_arguments = ', '.join(arguments)
+        repr_arguments = repr_arguments or 'void'
+        if tp.abi:
+            abi = tp.abi + ' '
+        else:
+            abi = ''
+        name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
+        prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
+        prnt('{')
+        call_arguments = ', '.join(call_arguments)
+        result_code = 'return '
+        if isinstance(tp.result, model.VoidType):
+            result_code = ''
+        prnt('  %s%s(%s);' % (result_code, name, call_arguments))
+        prnt('}')
+        #
+        prnt('#ifndef PYPY_VERSION')        # ------------------------------
+        #
+        prnt('static PyObject *')
+        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
+        prnt('{')
+        #
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arg = type.get_c_name(' x%d' % i, context)
+            prnt('  %s;' % arg)
+        #
+        localvars = set()
+        freelines = set()
+        for type in tp.args:
+            self._extra_local_variables(type, localvars, freelines)
+        for decl in sorted(localvars):
+            prnt('  %s;' % (decl,))
+        #
+        if not isinstance(tp.result, model.VoidType):
+            result_code = 'result = '
+            context = 'result of %s' % name
+            result_decl = '  %s;' % tp.result.get_c_name(' result', context)
+            prnt(result_decl)
+            prnt('  PyObject *pyresult;')
+        else:
+            result_decl = None
+            result_code = ''
+        #
+        if len(tp.args) > 1:
+            rng = range(len(tp.args))
+            for i in rng:
+                prnt('  PyObject *arg%d;' % i)
+            prnt()
+            prnt('  if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (
+                name, len(rng), len(rng),
+                ', '.join(['&arg%d' % i for i in rng])))
+            prnt('    return NULL;')
+        prnt()
+        #
+        for i, type in enumerate(tp.args):
+            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
+                                       'return NULL')
+            prnt()
+        #
+        prnt('  Py_BEGIN_ALLOW_THREADS')
+        prnt('  _cffi_restore_errno();')
+        call_arguments = ['x%d' % i for i in range(len(tp.args))]
+        call_arguments = ', '.join(call_arguments)
+        prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
+        prnt('  _cffi_save_errno();')
+        prnt('  Py_END_ALLOW_THREADS')
+        prnt()
+        #
+        prnt('  (void)self; /* unused */')
+        if numargs == 0:
+            prnt('  (void)noarg; /* unused */')
+        if result_code:
+            prnt('  pyresult = %s;' %
+                 self._convert_expr_from_c(tp.result, 'result', 'result type'))
+            for freeline in freelines:
+                prnt('  ' + freeline)
+            prnt('  return pyresult;')
+        else:
+            for freeline in freelines:
+                prnt('  ' + freeline)
+            prnt('  Py_INCREF(Py_None);')
+            prnt('  return Py_None;')
+        prnt('}')
+        #
+        prnt('#else')        # ------------------------------
+        #
+        # the PyPy version: need to replace struct/union arguments with
+        # pointers, and if the result is a struct/union, insert a first
+        # arg that is a pointer to the result.  We also do that for
+        # complex args and return type.
+        def need_indirection(type):
+            return (isinstance(type, model.StructOrUnion) or
+                    (isinstance(type, model.PrimitiveType) and
+                     type.is_complex_type()))
+        difference = False
+        arguments = []
+        call_arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            indirection = ''
+            if need_indirection(type):
+                indirection = '*'
+                difference = True
+            arg = type.get_c_name(' %sx%d' % (indirection, i), context)
+            arguments.append(arg)
+            call_arguments.append('%sx%d' % (indirection, i))
+        tp_result = tp.result
+        if need_indirection(tp_result):
+            context = 'result of %s' % name
+            arg = tp_result.get_c_name(' *result', context)
+            arguments.insert(0, arg)
+            tp_result = model.void_type
+            result_decl = None
+            result_code = '*result = '
+            difference = True
+        if difference:
+            repr_arguments = ', '.join(arguments)
+            repr_arguments = repr_arguments or 'void'
+            name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
+                                                       repr_arguments)
+            prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
+            prnt('{')
+            if result_decl:
+                prnt(result_decl)
+            call_arguments = ', '.join(call_arguments)
+            prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
+            if result_decl:
+                prnt('  return result;')
+            prnt('}')
+        else:
+            prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))
+        #
+        prnt('#endif')        # ------------------------------
+        prnt()
+
+    def _generate_cpy_function_ctx(self, tp, name):
+        if tp.ellipsis and not self.target_is_python:
+            self._generate_cpy_constant_ctx(tp, name)
+            return
+        type_index = self._typesdict[tp.as_raw_function()]
+        numargs = len(tp.args)
+        if self.target_is_python:
+            meth_kind = OP_DLOPEN_FUNC
+        elif numargs == 0:
+            meth_kind = OP_CPYTHON_BLTN_N   # 'METH_NOARGS'
+        elif numargs == 1:
+            meth_kind = OP_CPYTHON_BLTN_O   # 'METH_O'
+        else:
+            meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_f_%s' % name,
+                       CffiOp(meth_kind, type_index),
+                       size='_cffi_d_%s' % name))
+
+    # ----------
+    # named structs or unions
+
+    def _field_type(self, tp_struct, field_name, tp_field):
+        if isinstance(tp_field, model.ArrayType):
+            actual_length = tp_field.length
+            if actual_length == '...':
+                ptr_struct_name = tp_struct.get_c_name('*')
+                actual_length = '_cffi_array_len(((%s)0)->%s)' % (
+                    ptr_struct_name, field_name)
+            tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
+                                       tp_field.item)
+            tp_field = model.ArrayType(tp_item, actual_length)
+        return tp_field
+
+    def _struct_collecttype(self, tp):
+        self._do_collect_type(tp)
+        if self.target_is_python:
+            # also requires nested anon struct/unions in ABI mode, recursively
+            for fldtype in tp.anonymous_struct_fields():
+                self._struct_collecttype(fldtype)
+
+    def _struct_decl(self, tp, cname, approxname):
+        if tp.fldtypes is None:
+            return
+        prnt = self._prnt
+        checkfuncname = '_cffi_checkfld_%s' % (approxname,)
+        prnt('_CFFI_UNUSED_FN')
+        prnt('static void %s(%s *p)' % (checkfuncname, cname))
+        prnt('{')
+        prnt('  /* only to generate compile-time warnings or errors */')
+        prnt('  (void)p;')
+        for fname, ftype, fbitsize, fqual in self._enum_fields(tp):
+            try:
+                if ftype.is_integer_type() or fbitsize >= 0:
+                    # accept all integers, but complain on float or double
+                    if fname != '':
+                        prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "
+                             "an integer */" % (fname, cname, fname))
+                    continue
+                # only accept exactly the type declared, except that '[]'
+                # is interpreted as a '*' and so will match any array length.
+                # (It would also match '*', but that's harder to detect...)
+                while (isinstance(ftype, model.ArrayType)
+                       and (ftype.length is None or ftype.length == '...')):
+                    ftype = ftype.item
+                    fname = fname + '[0]'
+                prnt('  { %s = &p->%s; (void)tmp; }' % (
+                    ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
+                    fname))
+            except VerificationError as e:
+                prnt('  /* %s */' % str(e))   # cannot verify it, ignore
+        prnt('}')
+        prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
+        prnt()
+
+    def _struct_ctx(self, tp, cname, approxname, named_ptr=None):
+        type_index = self._typesdict[tp]
+        reason_for_not_expanding = None
+        flags = []
+        if isinstance(tp, model.UnionType):
+            flags.append("_CFFI_F_UNION")
+        if tp.fldtypes is None:
+            flags.append("_CFFI_F_OPAQUE")
+            reason_for_not_expanding = "opaque"
+        if (tp not in self.ffi._parser._included_declarations and
+                (named_ptr is None or
+                 named_ptr not in self.ffi._parser._included_declarations)):
+            if tp.fldtypes is None:
+                pass    # opaque
+            elif tp.partial or any(tp.anonymous_struct_fields()):
+                pass    # field layout obtained silently from the C compiler
+            else:
+                flags.append("_CFFI_F_CHECK_FIELDS")
+            if tp.packed:
+                if tp.packed > 1:
+                    raise NotImplementedError(
+                        "%r is declared with 'pack=%r'; only 0 or 1 are "
+                        "supported in API mode (try to use \"...;\", which "
+                        "does not require a 'pack' declaration)" %
+                        (tp, tp.packed))
+                flags.append("_CFFI_F_PACKED")
+        else:
+            flags.append("_CFFI_F_EXTERNAL")
+            reason_for_not_expanding = "external"
+        flags = '|'.join(flags) or '0'
+        c_fields = []
+        if reason_for_not_expanding is None:
+            enumfields = list(self._enum_fields(tp))
+            for fldname, fldtype, fbitsize, fqual in enumfields:
+                fldtype = self._field_type(tp, fldname, fldtype)
+                self._check_not_opaque(fldtype,
+                                       "field '%s.%s'" % (tp.name, fldname))
+                # cname is None for _add_missing_struct_unions() only
+                op = OP_NOOP
+                if fbitsize >= 0:
+                    op = OP_BITFIELD
+                    size = '%d /* bits */' % fbitsize
+                elif cname is None or (
+                        isinstance(fldtype, model.ArrayType) and
+                        fldtype.length is None):
+                    size = '(size_t)-1'
+                else:
+                    size = 'sizeof(((%s)0)->%s)' % (
+                        tp.get_c_name('*') if named_ptr is None
+                                           else named_ptr.name,
+                        fldname)
+                if cname is None or fbitsize >= 0:
+                    offset = '(size_t)-1'
+                elif named_ptr is not None:
+                    offset = '((char *)&((%s)4096)->%s) - (char *)4096' % (
+                        named_ptr.name, fldname)
+                else:
+                    offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
+                c_fields.append(
+                    FieldExpr(fldname, offset, size, fbitsize,
+                              CffiOp(op, self._typesdict[fldtype])))
+            first_field_index = len(self._lsts["field"])
+            self._lsts["field"].extend(c_fields)
+            #
+            if cname is None:  # unknown name, for _add_missing_struct_unions
+                size = '(size_t)-2'
+                align = -2
+                comment = "unnamed"
+            else:
+                if named_ptr is not None:
+                    size = 'sizeof(*(%s)0)' % (named_ptr.name,)
+                    align = '-1 /* unknown alignment */'
+                else:
+                    size = 'sizeof(%s)' % (cname,)
+                    align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)
+                comment = None
+        else:
+            size = '(size_t)-1'
+            align = -1
+            first_field_index = -1
+            comment = reason_for_not_expanding
+        self._lsts["struct_union"].append(
+            StructUnionExpr(tp.name, type_index, flags, size, align, comment,
+                            first_field_index, c_fields))
+        self._seen_struct_unions.add(tp)
+
+    def _check_not_opaque(self, tp, location):
+        while isinstance(tp, model.ArrayType):
+            tp = tp.item
+        if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:
+            raise TypeError(
+                "%s is of an opaque type (not declared in cdef())" % location)
+
+    def _add_missing_struct_unions(self):
+        # not very nice, but some struct declarations might be missing
+        # because they don't have any known C name.  Check that they are
+        # not partial (we can't complete or verify them!) and emit them
+        # anonymously.
+        lst = list(self._struct_unions.items())
+        lst.sort(key=lambda tp_order: tp_order[1])
+        for tp, order in lst:
+            if tp not in self._seen_struct_unions:
+                if tp.partial:
+                    raise NotImplementedError("internal inconsistency: %r is "
+                                              "partial but was not seen at "
+                                              "this point" % (tp,))
+                if tp.name.startswith('$') and tp.name[1:].isdigit():
+                    approxname = tp.name[1:]
+                elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':
+                    approxname = 'FILE'
+                    self._typedef_ctx(tp, 'FILE')
+                else:
+                    raise NotImplementedError("internal inconsistency: %r" %
+                                              (tp,))
+                self._struct_ctx(tp, None, approxname)
+
+    def _generate_cpy_struct_collecttype(self, tp, name):
+        self._struct_collecttype(tp)
+    _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
+
+    def _struct_names(self, tp):
+        cname = tp.get_c_name('')
+        if ' ' in cname:
+            return cname, cname.replace(' ', '_')
+        else:
+            return cname, '_' + cname
+
+    def _generate_cpy_struct_decl(self, tp, name):
+        self._struct_decl(tp, *self._struct_names(tp))
+    _generate_cpy_union_decl = _generate_cpy_struct_decl
+
+    def _generate_cpy_struct_ctx(self, tp, name):
+        self._struct_ctx(tp, *self._struct_names(tp))
+    _generate_cpy_union_ctx = _generate_cpy_struct_ctx
+
+    # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    def _generate_cpy_anonymous_collecttype(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_collecttype(tp, name)
+        else:
+            self._struct_collecttype(tp)
+
+    def _generate_cpy_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_decl(tp)
+        else:
+            self._struct_decl(tp, name, 'typedef_' + name)
+
+    def _generate_cpy_anonymous_ctx(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._enum_ctx(tp, name)
+        else:
+            self._struct_ctx(tp, name, 'typedef_' + name)
+
+    # ----------
+    # constants, declared with "static const ..."
+
+    def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+                            check_value=None):
+        if (category, name) in self._seen_constants:
+            raise VerificationError(
+                "duplicate declaration of %s '%s'" % (category, name))
+        self._seen_constants.add((category, name))
+        #
+        prnt = self._prnt
+        funcname = '_cffi_%s_%s' % (category, name)
+        if is_int:
+            prnt('static int %s(unsigned long long *o)' % funcname)
+            prnt('{')
+            prnt('  int n = (%s) <= 0;' % (name,))
+            prnt('  *o = (unsigned long long)((%s) | 0);'
+                 '  /* check that %s is an integer */' % (name, name))
+            if check_value is not None:
+                if check_value > 0:
+                    check_value = '%dU' % (check_value,)
+                prnt('  if (!_cffi_check_int(*o, n, %s))' % (check_value,))
+                prnt('    n |= 2;')
+            prnt('  return n;')
+            prnt('}')
+        else:
+            assert check_value is None
+            prnt('static void %s(char *o)' % funcname)
+            prnt('{')
+            prnt('  *(%s)o = %s;' % (tp.get_c_name('*'), name))
+            prnt('}')
+        prnt()
+
+    def _generate_cpy_constant_collecttype(self, tp, name):
+        is_int = tp.is_integer_type()
+        if not is_int or self.target_is_python:
+            self._do_collect_type(tp)
+
+    def _generate_cpy_constant_decl(self, tp, name):
+        is_int = tp.is_integer_type()
+        self._generate_cpy_const(is_int, name, tp)
+
+    def _generate_cpy_constant_ctx(self, tp, name):
+        if not self.target_is_python and tp.is_integer_type():
+            type_op = CffiOp(OP_CONSTANT_INT, -1)
+        else:
+            if self.target_is_python:
+                const_kind = OP_DLOPEN_CONST
+            else:
+                const_kind = OP_CONSTANT
+            type_index = self._typesdict[tp]
+            type_op = CffiOp(const_kind, type_index)
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_const_%s' % name, type_op))
+
+    # ----------
+    # enums
+
+    def _generate_cpy_enum_collecttype(self, tp, name):
+        self._do_collect_type(tp)
+
+    def _generate_cpy_enum_decl(self, tp, name=None):
+        for enumerator in tp.enumerators:
+            self._generate_cpy_const(True, enumerator)
+
+    def _enum_ctx(self, tp, cname):
+        type_index = self._typesdict[tp]
+        type_op = CffiOp(OP_ENUM, -1)
+        if self.target_is_python:
+            tp.check_not_partial()
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            self._lsts["global"].append(
+                GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,
+                           check_value=enumvalue))
+        #
+        if cname is not None and '$' not in cname and not self.target_is_python:
+            size = "sizeof(%s)" % cname
+            signed = "((%s)-1) <= 0" % cname
+        else:
+            basetp = tp.build_baseinttype(self.ffi, [])
+            size = self.ffi.sizeof(basetp)
+            signed = int(int(self.ffi.cast(basetp, -1)) < 0)
+        allenums = ",".join(tp.enumerators)
+        self._lsts["enum"].append(
+            EnumExpr(tp.name, type_index, size, signed, allenums))
+
+    def _generate_cpy_enum_ctx(self, tp, name):
+        self._enum_ctx(tp, tp._get_c_name())
+
+    # ----------
+    # macros: for now only for integers
+
+    def _generate_cpy_macro_collecttype(self, tp, name):
+        pass
+
+    def _generate_cpy_macro_decl(self, tp, name):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        self._generate_cpy_const(True, name, check_value=check_value)
+
+    def _generate_cpy_macro_ctx(self, tp, name):
+        if tp == '...':
+            if self.target_is_python:
+                raise VerificationError(
+                    "cannot use the syntax '...' in '#define %s ...' when "
+                    "using the ABI mode" % (name,))
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        type_op = CffiOp(OP_CONSTANT_INT, -1)
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_const_%s' % name, type_op,
+                       check_value=check_value))
+
+    # ----------
+    # global variables
+
+    def _global_type(self, tp, global_name):
+        if isinstance(tp, model.ArrayType):
+            actual_length = tp.length
+            if actual_length == '...':
+                actual_length = '_cffi_array_len(%s)' % (global_name,)
+            tp_item = self._global_type(tp.item, '%s[0]' % global_name)
+            tp = model.ArrayType(tp_item, actual_length)
+        return tp
+
+    def _generate_cpy_variable_collecttype(self, tp, name):
+        self._do_collect_type(self._global_type(tp, name))
+
+    def _generate_cpy_variable_decl(self, tp, name):
+        prnt = self._prnt
+        tp = self._global_type(tp, name)
+        if isinstance(tp, model.ArrayType) and tp.length is None:
+            tp = tp.item
+            ampersand = ''
+        else:
+            ampersand = '&'
+        # This code assumes that casts from "tp *" to "void *" is a
+        # no-op, i.e. a function that returns a "tp *" can be called
+        # as if it returned a "void *".  This should be generally true
+        # on any modern machine.  The only exception to that rule (on
+        # uncommon architectures, and as far as I can tell) might be
+        # if 'tp' were a function type, but that is not possible here.
+        # (If 'tp' is a function _pointer_ type, then casts from "fn_t
+        # **" to "void *" are again no-ops, as far as I can tell.)
+        decl = '*_cffi_var_%s(void)' % (name,)
+        prnt('static ' + tp.get_c_name(decl, quals=self._current_quals))
+        prnt('{')
+        prnt('  return %s(%s);' % (ampersand, name))
+        prnt('}')
+        prnt()
+
+    def _generate_cpy_variable_ctx(self, tp, name):
+        tp = self._global_type(tp, name)
+        type_index = self._typesdict[tp]
+        if self.target_is_python:
+            op = OP_GLOBAL_VAR
+        else:
+            op = OP_GLOBAL_VAR_F
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index)))
+
+    # ----------
+    # extern "Python"
+
+    def _generate_cpy_extern_python_collecttype(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        self._do_collect_type(tp)
+    _generate_cpy_dllexport_python_collecttype = \
+      _generate_cpy_extern_python_plus_c_collecttype = \
+      _generate_cpy_extern_python_collecttype
+
+    def _extern_python_decl(self, tp, name, tag_and_space):
+        prnt = self._prnt
+        if isinstance(tp.result, model.VoidType):
+            size_of_result = '0'
+        else:
+            context = 'result of %s' % name
+            size_of_result = '(int)sizeof(%s)' % (
+                tp.result.get_c_name('', context),)
+        prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
+        prnt('  { "%s.%s", %s, 0, 0 };' % (
+            self.module_name, name, size_of_result))
+        prnt()
+        #
+        arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arg = type.get_c_name(' a%d' % i, context)
+            arguments.append(arg)
+        #
+        repr_arguments = ', '.join(arguments)
+        repr_arguments = repr_arguments or 'void'
+        name_and_arguments = '%s(%s)' % (name, repr_arguments)
+        if tp.abi == "__stdcall":
+            name_and_arguments = '_cffi_stdcall ' + name_and_arguments
+        #
+        def may_need_128_bits(tp):
+            return (isinstance(tp, model.PrimitiveType) and
+                    tp.name == 'long double')
+        #
+        size_of_a = max(len(tp.args)*8, 8)
+        if may_need_128_bits(tp.result):
+            size_of_a = max(size_of_a, 16)
+        if isinstance(tp.result, model.StructOrUnion):
+            size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
+                tp.result.get_c_name(''), size_of_a,
+                tp.result.get_c_name(''), size_of_a)
+        prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
+        prnt('{')
+        prnt('  char a[%s];' % size_of_a)
+        prnt('  char *p = a;')
+        for i, type in enumerate(tp.args):
+            arg = 'a%d' % i
+            if (isinstance(type, model.StructOrUnion) or
+                    may_need_128_bits(type)):
+                arg = '&' + arg
+                type = model.PointerType(type)
+            prnt('  *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg))
+        prnt('  _cffi_call_python(&_cffi_externpy__%s, p);' % name)
+        if not isinstance(tp.result, model.VoidType):
+            prnt('  return *(%s)p;' % (tp.result.get_c_name('*'),))
+        prnt('}')
+        prnt()
+        self._num_externpy += 1
+
+    def _generate_cpy_extern_python_decl(self, tp, name):
+        self._extern_python_decl(tp, name, 'static ')
+
+    def _generate_cpy_dllexport_python_decl(self, tp, name):
+        self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')
+
+    def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
+        self._extern_python_decl(tp, name, '')
+
+    def _generate_cpy_extern_python_ctx(self, tp, name):
+        if self.target_is_python:
+            raise VerificationError(
+                "cannot use 'extern \"Python\"' in the ABI mode")
+        if tp.ellipsis:
+            raise NotImplementedError("a vararg function is extern \"Python\"")
+        type_index = self._typesdict[tp]
+        type_op = CffiOp(OP_EXTERN_PYTHON, type_index)
+        self._lsts["global"].append(
+            GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
+
+    _generate_cpy_dllexport_python_ctx = \
+      _generate_cpy_extern_python_plus_c_ctx = \
+      _generate_cpy_extern_python_ctx
+
+    def _print_string_literal_in_array(self, s):
+        prnt = self._prnt
+        prnt('// # NB. this is not a string because of a size limit in MSVC')
+        if not isinstance(s, bytes):    # unicode
+            s = s.encode('utf-8')       # -> bytes
+        else:
+            s.decode('utf-8')           # got bytes, check for valid utf-8
+        try:
+            s.decode('ascii')
+        except UnicodeDecodeError:
+            s = b'# -*- encoding: utf8 -*-\n' + s
+        for line in s.splitlines(True):
+            comment = line
+            if type('//') is bytes:     # python2
+                line = map(ord, line)   #     make a list of integers
+            else:                       # python3
+                # type(line) is bytes, which enumerates like a list of integers
+                comment = ascii(comment)[1:-1]
+            prnt(('// ' + comment).rstrip())
+            printed_line = ''
+            for c in line:
+                if len(printed_line) >= 76:
+                    prnt(printed_line)
+                    printed_line = ''
+                printed_line += '%d,' % (c,)
+            prnt(printed_line)
+
+    # ----------
+    # emitting the opcodes for individual types
+
+    def _emit_bytecode_VoidType(self, tp, index):
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID)
+
+    def _emit_bytecode_PrimitiveType(self, tp, index):
+        prim_index = PRIMITIVE_TO_INDEX[tp.name]
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
+
+    def _emit_bytecode_UnknownIntegerType(self, tp, index):
+        s = ('_cffi_prim_int(sizeof(%s), (\n'
+             '           ((%s)-1) | 0 /* check that %s is an integer type */\n'
+             '         ) <= 0)' % (tp.name, tp.name, tp.name))
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+
+    def _emit_bytecode_UnknownFloatType(self, tp, index):
+        s = ('_cffi_prim_float(sizeof(%s) *\n'
+             '           (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
+             '         )' % (tp.name, tp.name))
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+
+    def _emit_bytecode_RawFunctionType(self, tp, index):
+        self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
+        index += 1
+        for tp1 in tp.args:
+            realindex = self._typesdict[tp1]
+            if index != realindex:
+                if isinstance(tp1, model.PrimitiveType):
+                    self._emit_bytecode_PrimitiveType(tp1, index)
+                else:
+                    self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
+            index += 1
+        flags = int(tp.ellipsis)
+        if tp.abi is not None:
+            if tp.abi == '__stdcall':
+                flags |= 2
+            else:
+                raise NotImplementedError("abi=%r" % (tp.abi,))
+        self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
+
+    def _emit_bytecode_PointerType(self, tp, index):
+        self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
+
+    _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType
+    _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType
+
+    def _emit_bytecode_FunctionPtrType(self, tp, index):
+        raw = tp.as_raw_function()
+        self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw])
+
+    def _emit_bytecode_ArrayType(self, tp, index):
+        item_index = self._typesdict[tp.item]
+        if tp.length is None:
+            self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
+        elif tp.length == '...':
+            raise VerificationError(
+                "type %s badly placed: the '...' array length can only be "
+                "used on global arrays or on fields of structures" % (
+                    str(tp).replace('/*...*/', '...'),))
+        else:
+            assert self.cffi_types[index + 1] == 'LEN'
+            self.cffi_types[index] = CffiOp(OP_ARRAY, item_index)
+            self.cffi_types[index + 1] = CffiOp(None, str(tp.length))
+
+    def _emit_bytecode_StructType(self, tp, index):
+        struct_index = self._struct_unions[tp]
+        self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index)
+    _emit_bytecode_UnionType = _emit_bytecode_StructType
+
+    def _emit_bytecode_EnumType(self, tp, index):
+        enum_index = self._enums[tp]
+        self.cffi_types[index] = CffiOp(OP_ENUM, enum_index)
+
+
+if sys.version_info >= (3,):
+    NativeIO = io.StringIO
+else:
+    class NativeIO(io.BytesIO):
+        def write(self, s):
+            if isinstance(s, unicode):
+                s = s.encode('ascii')
+            super(NativeIO, self).write(s)
+
+def _is_file_like(maybefile):
+    # compare to xml.etree.ElementTree._get_writer
+    return hasattr(maybefile, 'write')
+
+def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
+    if verbose:
+        print("generating %s" % (target_file,))
+    recompiler = Recompiler(ffi, module_name,
+                            target_is_python=(preamble is None))
+    recompiler.collect_type_table()
+    recompiler.collect_step_tables()
+    if _is_file_like(target_file):
+        recompiler.write_source_to_f(target_file, preamble)
+        return True
+    f = NativeIO()
+    recompiler.write_source_to_f(f, preamble)
+    output = f.getvalue()
+    try:
+        with open(target_file, 'r') as f1:
+            if f1.read(len(output) + 1) != output:
+                raise IOError
+        if verbose:
+            print("(already up-to-date)")
+        return False     # already up-to-date
+    except IOError:
+        tmp_file = '%s.~%d' % (target_file, os.getpid())
+        with open(tmp_file, 'w') as f1:
+            f1.write(output)
+        try:
+            os.rename(tmp_file, target_file)
+        except OSError:
+            os.unlink(target_file)
+            os.rename(tmp_file, target_file)
+        return True
+
+def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
+    assert preamble is not None
+    return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
+                                verbose)
+
+def make_py_source(ffi, module_name, target_py_file, verbose=False):
+    return _make_c_or_py_source(ffi, module_name, None, target_py_file,
+                                verbose)
+
+def _modname_to_file(outputdir, modname, extension):
+    parts = modname.split('.')
+    try:
+        os.makedirs(os.path.join(outputdir, *parts[:-1]))
+    except OSError:
+        pass
+    parts[-1] += extension
+    return os.path.join(outputdir, *parts), parts
+
+
+# Aaargh.  Distutils is not tested at all for the purpose of compiling
+# DLLs that are not extension modules.  Here are some hacks to work
+# around that, in the _patch_for_*() functions...
+
+def _patch_meth(patchlist, cls, name, new_meth):
+    old = getattr(cls, name)
+    patchlist.append((cls, name, old))
+    setattr(cls, name, new_meth)
+    return old
+
+def _unpatch_meths(patchlist):
+    for cls, name, old_meth in reversed(patchlist):
+        setattr(cls, name, old_meth)
+
+def _patch_for_embedding(patchlist):
+    if sys.platform == 'win32':
+        # we must not remove the manifest when building for embedding!
+        # FUTURE: this module was removed in setuptools 74; this is likely dead code and should be removed,
+        #  since the toolchain it supports (VS2005-2008) is also long dead.
+        from cffi._shimmed_dist_utils import MSVCCompiler
+        if MSVCCompiler is not None:
+            _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
+                        lambda self, manifest_file: manifest_file)
+
+    if sys.platform == 'darwin':
+        # we must not make a '-bundle', but a '-dynamiclib' instead
+        from cffi._shimmed_dist_utils import CCompiler
+        def my_link_shared_object(self, *args, **kwds):
+            if '-bundle' in self.linker_so:
+                self.linker_so = list(self.linker_so)
+                i = self.linker_so.index('-bundle')
+                self.linker_so[i] = '-dynamiclib'
+            return old_link_shared_object(self, *args, **kwds)
+        old_link_shared_object = _patch_meth(patchlist, CCompiler,
+                                             'link_shared_object',
+                                             my_link_shared_object)
+
+def _patch_for_target(patchlist, target):
+    from cffi._shimmed_dist_utils import build_ext
+    # if 'target' is different from '*', we need to patch some internal
+    # method to just return this 'target' value, instead of having it
+    # built from module_name
+    if target.endswith('.*'):
+        target = target[:-2]
+        if sys.platform == 'win32':
+            target += '.dll'
+        elif sys.platform == 'darwin':
+            target += '.dylib'
+        else:
+            target += '.so'
+    _patch_meth(patchlist, build_ext, 'get_ext_filename',
+                lambda self, ext_name: target)
+
+
+def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
+              c_file=None, source_extension='.c', extradir=None,
+              compiler_verbose=1, target=None, debug=None,
+              uses_ffiplatform=True, **kwds):
+    if not isinstance(module_name, str):
+        module_name = module_name.encode('ascii')
+    if ffi._windows_unicode:
+        ffi._apply_windows_unicode(kwds)
+    if preamble is not None:
+        if call_c_compiler and _is_file_like(c_file):
+            raise TypeError("Writing to file-like objects is not supported "
+                            "with call_c_compiler=True")
+        embedding = (ffi._embedding is not None)
+        if embedding:
+            ffi._apply_embedding_fix(kwds)
+        if c_file is None:
+            c_file, parts = _modname_to_file(tmpdir, module_name,
+                                             source_extension)
+            if extradir:
+                parts = [extradir] + parts
+            ext_c_file = os.path.join(*parts)
+        else:
+            ext_c_file = c_file
+        #
+        if target is None:
+            if embedding:
+                target = '%s.*' % module_name
+            else:
+                target = '*'
+        #
+        if uses_ffiplatform:
+            ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
+        else:
+            ext = None
+        updated = make_c_source(ffi, module_name, preamble, c_file,
+                                verbose=compiler_verbose)
+        if call_c_compiler:
+            patchlist = []
+            cwd = os.getcwd()
+            try:
+                if embedding:
+                    _patch_for_embedding(patchlist)
+                if target != '*':
+                    _patch_for_target(patchlist, target)
+                if compiler_verbose:
+                    if tmpdir == '.':
+                        msg = 'the current directory is'
+                    else:
+                        msg = 'setting the current directory to'
+                    print('%s %r' % (msg, os.path.abspath(tmpdir)))
+                os.chdir(tmpdir)
+                outputfilename = ffiplatform.compile('.', ext,
+                                                     compiler_verbose, debug)
+            finally:
+                os.chdir(cwd)
+                _unpatch_meths(patchlist)
+            return outputfilename
+        else:
+            return ext, updated
+    else:
+        if c_file is None:
+            c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
+        updated = make_py_source(ffi, module_name, c_file,
+                                 verbose=compiler_verbose)
+        if call_c_compiler:
+            return c_file
+        else:
+            return None, updated
+
diff --git a/.venv/lib/python3.8/site-packages/cffi/setuptools_ext.py b/.venv/lib/python3.8/site-packages/cffi/setuptools_ext.py
new file mode 100644
index 0000000000000000000000000000000000000000..681b49d7ad964d9de4b6b32a24eec6fcebddf7ed
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/setuptools_ext.py
@@ -0,0 +1,216 @@
+import os
+import sys
+
+try:
+    basestring
+except NameError:
+    # Python 3.x
+    basestring = str
+
+def error(msg):
+    from cffi._shimmed_dist_utils import DistutilsSetupError
+    raise DistutilsSetupError(msg)
+
+
+def execfile(filename, glob):
+    # We use execfile() (here rewritten for Python 3) instead of
+    # __import__() to load the build script.  The problem with
+    # a normal import is that in some packages, the intermediate
+    # __init__.py files may already try to import the file that
+    # we are generating.
+    with open(filename) as f:
+        src = f.read()
+    src += '\n'      # Python 2.6 compatibility
+    code = compile(src, filename, 'exec')
+    exec(code, glob, glob)
+
+
+def add_cffi_module(dist, mod_spec):
+    from cffi.api import FFI
+
+    if not isinstance(mod_spec, basestring):
+        error("argument to 'cffi_modules=...' must be a str or a list of str,"
+              " not %r" % (type(mod_spec).__name__,))
+    mod_spec = str(mod_spec)
+    try:
+        build_file_name, ffi_var_name = mod_spec.split(':')
+    except ValueError:
+        error("%r must be of the form 'path/build.py:ffi_variable'" %
+              (mod_spec,))
+    if not os.path.exists(build_file_name):
+        ext = ''
+        rewritten = build_file_name.replace('.', '/') + '.py'
+        if os.path.exists(rewritten):
+            ext = ' (rewrite cffi_modules to [%r])' % (
+                rewritten + ':' + ffi_var_name,)
+        error("%r does not name an existing file%s" % (build_file_name, ext))
+
+    mod_vars = {'__name__': '__cffi__', '__file__': build_file_name}
+    execfile(build_file_name, mod_vars)
+
+    try:
+        ffi = mod_vars[ffi_var_name]
+    except KeyError:
+        error("%r: object %r not found in module" % (mod_spec,
+                                                     ffi_var_name))
+    if not isinstance(ffi, FFI):
+        ffi = ffi()      # maybe it's a function instead of directly an ffi
+    if not isinstance(ffi, FFI):
+        error("%r is not an FFI instance (got %r)" % (mod_spec,
+                                                      type(ffi).__name__))
+    if not hasattr(ffi, '_assigned_source'):
+        error("%r: the set_source() method was not called" % (mod_spec,))
+    module_name, source, source_extension, kwds = ffi._assigned_source
+    if ffi._windows_unicode:
+        kwds = kwds.copy()
+        ffi._apply_windows_unicode(kwds)
+
+    if source is None:
+        _add_py_module(dist, ffi, module_name)
+    else:
+        _add_c_module(dist, ffi, module_name, source, source_extension, kwds)
+
+def _set_py_limited_api(Extension, kwds):
+    """
+    Add py_limited_api to kwds if setuptools >= 26 is in use.
+    Do not alter the setting if it already exists.
+    Setuptools takes care of ignoring the flag on Python 2 and PyPy.
+
+    CPython itself should ignore the flag in a debugging version
+    (by not listing .abi3.so in the extensions it supports), but
+    it doesn't so far, creating troubles.  That's why we check
+    for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
+    of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)
+
+    On Windows, with CPython <= 3.4, it's better not to use py_limited_api
+    because virtualenv *still* doesn't copy PYTHON3.DLL on these versions.
+    Recently (2020) we started shipping only >= 3.5 wheels, though.  So
+    we'll give it another try and set py_limited_api on Windows >= 3.5.
+    """
+    from cffi import recompiler
+
+    if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
+            and recompiler.USE_LIMITED_API):
+        import setuptools
+        try:
+            setuptools_major_version = int(setuptools.__version__.partition('.')[0])
+            if setuptools_major_version >= 26:
+                kwds['py_limited_api'] = True
+        except ValueError:  # certain development versions of setuptools
+            # If we don't know the version number of setuptools, we
+            # try to set 'py_limited_api' anyway.  At worst, we get a
+            # warning.
+            kwds['py_limited_api'] = True
+    return kwds
+
+def _add_c_module(dist, ffi, module_name, source, source_extension, kwds):
+    # We are a setuptools extension. Need this build_ext for py_limited_api.
+    from setuptools.command.build_ext import build_ext
+    from cffi._shimmed_dist_utils import Extension, log, mkpath
+    from cffi import recompiler
+
+    allsources = ['$PLACEHOLDER']
+    allsources.extend(kwds.pop('sources', []))
+    kwds = _set_py_limited_api(Extension, kwds)
+    ext = Extension(name=module_name, sources=allsources, **kwds)
+
+    def make_mod(tmpdir, pre_run=None):
+        c_file = os.path.join(tmpdir, module_name + source_extension)
+        log.info("generating cffi module %r" % c_file)
+        mkpath(tmpdir)
+        # a setuptools-only, API-only hook: called with the "ext" and "ffi"
+        # arguments just before we turn the ffi into C code.  To use it,
+        # subclass the 'distutils.command.build_ext.build_ext' class and
+        # add a method 'def pre_run(self, ext, ffi)'.
+        if pre_run is not None:
+            pre_run(ext, ffi)
+        updated = recompiler.make_c_source(ffi, module_name, source, c_file)
+        if not updated:
+            log.info("already up-to-date")
+        return c_file
+
+    if dist.ext_modules is None:
+        dist.ext_modules = []
+    dist.ext_modules.append(ext)
+
+    base_class = dist.cmdclass.get('build_ext', build_ext)
+    class build_ext_make_mod(base_class):
+        def run(self):
+            if ext.sources[0] == '$PLACEHOLDER':
+                pre_run = getattr(self, 'pre_run', None)
+                ext.sources[0] = make_mod(self.build_temp, pre_run)
+            base_class.run(self)
+    dist.cmdclass['build_ext'] = build_ext_make_mod
+    # NB. multiple runs here will create multiple 'build_ext_make_mod'
+    # classes.  Even in this case the 'build_ext' command should be
+    # run once; but just in case, the logic above does nothing if
+    # called again.
+
+
+def _add_py_module(dist, ffi, module_name):
+    from setuptools.command.build_py import build_py
+    from setuptools.command.build_ext import build_ext
+    from cffi._shimmed_dist_utils import log, mkpath
+    from cffi import recompiler
+
+    def generate_mod(py_file):
+        log.info("generating cffi module %r" % py_file)
+        mkpath(os.path.dirname(py_file))
+        updated = recompiler.make_py_source(ffi, module_name, py_file)
+        if not updated:
+            log.info("already up-to-date")
+
+    base_class = dist.cmdclass.get('build_py', build_py)
+    class build_py_make_mod(base_class):
+        def run(self):
+            base_class.run(self)
+            module_path = module_name.split('.')
+            module_path[-1] += '.py'
+            generate_mod(os.path.join(self.build_lib, *module_path))
+        def get_source_files(self):
+            # This is called from 'setup.py sdist' only.  Exclude
+            # the generate .py module in this case.
+            saved_py_modules = self.py_modules
+            try:
+                if saved_py_modules:
+                    self.py_modules = [m for m in saved_py_modules
+                                         if m != module_name]
+                return base_class.get_source_files(self)
+            finally:
+                self.py_modules = saved_py_modules
+    dist.cmdclass['build_py'] = build_py_make_mod
+
+    # distutils and setuptools have no notion I could find of a
+    # generated python module.  If we don't add module_name to
+    # dist.py_modules, then things mostly work but there are some
+    # combination of options (--root and --record) that will miss
+    # the module.  So we add it here, which gives a few apparently
+    # harmless warnings about not finding the file outside the
+    # build directory.
+    # Then we need to hack more in get_source_files(); see above.
+    if dist.py_modules is None:
+        dist.py_modules = []
+    dist.py_modules.append(module_name)
+
+    # the following is only for "build_ext -i"
+    base_class_2 = dist.cmdclass.get('build_ext', build_ext)
+    class build_ext_make_mod(base_class_2):
+        def run(self):
+            base_class_2.run(self)
+            if self.inplace:
+                # from get_ext_fullpath() in distutils/command/build_ext.py
+                module_path = module_name.split('.')
+                package = '.'.join(module_path[:-1])
+                build_py = self.get_finalized_command('build_py')
+                package_dir = build_py.get_package_dir(package)
+                file_name = module_path[-1] + '.py'
+                generate_mod(os.path.join(package_dir, file_name))
+    dist.cmdclass['build_ext'] = build_ext_make_mod
+
+def cffi_modules(dist, attr, value):
+    assert attr == 'cffi_modules'
+    if isinstance(value, basestring):
+        value = [value]
+
+    for cffi_module in value:
+        add_cffi_module(dist, cffi_module)
diff --git a/.venv/lib/python3.8/site-packages/cffi/vengine_cpy.py b/.venv/lib/python3.8/site-packages/cffi/vengine_cpy.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb0b6f70e499159819ed01a392205752c1b29936
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/vengine_cpy.py
@@ -0,0 +1,1084 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
+import sys
+from . import model
+from .error import VerificationError
+from . import _imp_emulation as imp
+
+
+class VCPythonEngine(object):
+    _class_key = 'x'
+    _gen_python_module = True
+
+    def __init__(self, verifier):
+        self.verifier = verifier
+        self.ffi = verifier.ffi
+        self._struct_pending_verification = {}
+        self._types_of_builtin_functions = {}
+
+    def patch_extension_kwds(self, kwds):
+        pass
+
+    def find_module(self, module_name, path, so_suffixes):
+        try:
+            f, filename, descr = imp.find_module(module_name, path)
+        except ImportError:
+            return None
+        if f is not None:
+            f.close()
+        # Note that after a setuptools installation, there are both .py
+        # and .so files with the same basename.  The code here relies on
+        # imp.find_module() locating the .so in priority.
+        if descr[0] not in so_suffixes:
+            return None
+        return filename
+
+    def collect_types(self):
+        self._typesdict = {}
+        self._generate("collecttype")
+
+    def _prnt(self, what=''):
+        self._f.write(what + '\n')
+
+    def _gettypenum(self, type):
+        # a KeyError here is a bug.  please report it! :-)
+        return self._typesdict[type]
+
+    def _do_collect_type(self, tp):
+        if ((not isinstance(tp, model.PrimitiveType)
+             or tp.name == 'long double')
+                and tp not in self._typesdict):
+            num = len(self._typesdict)
+            self._typesdict[tp] = num
+
+    def write_source_to_f(self):
+        self.collect_types()
+        #
+        # The new module will have a _cffi_setup() function that receives
+        # objects from the ffi world, and that calls some setup code in
+        # the module.  This setup code is split in several independent
+        # functions, e.g. one per constant.  The functions are "chained"
+        # by ending in a tail call to each other.
+        #
+        # This is further split in two chained lists, depending on if we
+        # can do it at import-time or if we must wait for _cffi_setup() to
+        # provide us with the <ctype> objects.  This is needed because we
+        # need the values of the enum constants in order to build the
+        # <ctype 'enum'> that we may have to pass to _cffi_setup().
+        #
+        # The following two 'chained_list_constants' items contains
+        # the head of these two chained lists, as a string that gives the
+        # call to do, if any.
+        self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)']
+        #
+        prnt = self._prnt
+        # first paste some standard set of lines that are mostly '#define'
+        prnt(cffimod_header)
+        prnt()
+        # then paste the C source given by the user, verbatim.
+        prnt(self.verifier.preamble)
+        prnt()
+        #
+        # call generate_cpy_xxx_decl(), for every xxx found from
+        # ffi._parser._declarations.  This generates all the functions.
+        self._generate("decl")
+        #
+        # implement the function _cffi_setup_custom() as calling the
+        # head of the chained list.
+        self._generate_setup_custom()
+        prnt()
+        #
+        # produce the method table, including the entries for the
+        # generated Python->C function wrappers, which are done
+        # by generate_cpy_function_method().
+        prnt('static PyMethodDef _cffi_methods[] = {')
+        self._generate("method")
+        prnt('  {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},')
+        prnt('  {NULL, NULL, 0, NULL}    /* Sentinel */')
+        prnt('};')
+        prnt()
+        #
+        # standard init.
+        modname = self.verifier.get_module_name()
+        constants = self._chained_list_constants[False]
+        prnt('#if PY_MAJOR_VERSION >= 3')
+        prnt()
+        prnt('static struct PyModuleDef _cffi_module_def = {')
+        prnt('  PyModuleDef_HEAD_INIT,')
+        prnt('  "%s",' % modname)
+        prnt('  NULL,')
+        prnt('  -1,')
+        prnt('  _cffi_methods,')
+        prnt('  NULL, NULL, NULL, NULL')
+        prnt('};')
+        prnt()
+        prnt('PyMODINIT_FUNC')
+        prnt('PyInit_%s(void)' % modname)
+        prnt('{')
+        prnt('  PyObject *lib;')
+        prnt('  lib = PyModule_Create(&_cffi_module_def);')
+        prnt('  if (lib == NULL)')
+        prnt('    return NULL;')
+        prnt('  if (%s < 0 || _cffi_init() < 0) {' % (constants,))
+        prnt('    Py_DECREF(lib);')
+        prnt('    return NULL;')
+        prnt('  }')
+        prnt('  return lib;')
+        prnt('}')
+        prnt()
+        prnt('#else')
+        prnt()
+        prnt('PyMODINIT_FUNC')
+        prnt('init%s(void)' % modname)
+        prnt('{')
+        prnt('  PyObject *lib;')
+        prnt('  lib = Py_InitModule("%s", _cffi_methods);' % modname)
+        prnt('  if (lib == NULL)')
+        prnt('    return;')
+        prnt('  if (%s < 0 || _cffi_init() < 0)' % (constants,))
+        prnt('    return;')
+        prnt('  return;')
+        prnt('}')
+        prnt()
+        prnt('#endif')
+
+    def load_library(self, flags=None):
+        # XXX review all usages of 'self' here!
+        # import it as a new extension module
+        imp.acquire_lock()
+        try:
+            if hasattr(sys, "getdlopenflags"):
+                previous_flags = sys.getdlopenflags()
+            try:
+                if hasattr(sys, "setdlopenflags") and flags is not None:
+                    sys.setdlopenflags(flags)
+                module = imp.load_dynamic(self.verifier.get_module_name(),
+                                          self.verifier.modulefilename)
+            except ImportError as e:
+                error = "importing %r: %s" % (self.verifier.modulefilename, e)
+                raise VerificationError(error)
+            finally:
+                if hasattr(sys, "setdlopenflags"):
+                    sys.setdlopenflags(previous_flags)
+        finally:
+            imp.release_lock()
+        #
+        # call loading_cpy_struct() to get the struct layout inferred by
+        # the C compiler
+        self._load(module, 'loading')
+        #
+        # the C code will need the <ctype> objects.  Collect them in
+        # order in a list.
+        revmapping = dict([(value, key)
+                           for (key, value) in self._typesdict.items()])
+        lst = [revmapping[i] for i in range(len(revmapping))]
+        lst = list(map(self.ffi._get_cached_btype, lst))
+        #
+        # build the FFILibrary class and instance and call _cffi_setup().
+        # this will set up some fields like '_cffi_types', and only then
+        # it will invoke the chained list of functions that will really
+        # build (notably) the constant objects, as <cdata> if they are
+        # pointers, and store them as attributes on the 'library' object.
+        class FFILibrary(object):
+            _cffi_python_module = module
+            _cffi_ffi = self.ffi
+            _cffi_dir = []
+            def __dir__(self):
+                return FFILibrary._cffi_dir + list(self.__dict__)
+        library = FFILibrary()
+        if module._cffi_setup(lst, VerificationError, library):
+            import warnings
+            warnings.warn("reimporting %r might overwrite older definitions"
+                          % (self.verifier.get_module_name()))
+        #
+        # finally, call the loaded_cpy_xxx() functions.  This will perform
+        # the final adjustments, like copying the Python->C wrapper
+        # functions from the module to the 'library' object, and setting
+        # up the FFILibrary class with properties for the global C variables.
+        self._load(module, 'loaded', library=library)
+        module._cffi_original_ffi = self.ffi
+        module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions
+        return library
+
+    def _get_declarations(self):
+        lst = [(key, tp) for (key, (tp, qual)) in
+                                self.ffi._parser._declarations.items()]
+        lst.sort()
+        return lst
+
+    def _generate(self, step_name):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            try:
+                method = getattr(self, '_generate_cpy_%s_%s' % (kind,
+                                                                step_name))
+            except AttributeError:
+                raise VerificationError(
+                    "not implemented in verify(): %r" % name)
+            try:
+                method(tp, realname)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _load(self, module, step_name, **kwds):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
+            try:
+                method(tp, realname, module, **kwds)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _generate_nothing(self, tp, name):
+        pass
+
+    def _loaded_noop(self, tp, name, module, **kwds):
+        pass
+
+    # ----------
+
+    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
+        extraarg = ''
+        if isinstance(tp, model.PrimitiveType):
+            if tp.is_integer_type() and tp.name != '_Bool':
+                converter = '_cffi_to_c_int'
+                extraarg = ', %s' % tp.name
+            elif tp.is_complex_type():
+                raise VerificationError(
+                    "not implemented in verify(): complex types")
+            else:
+                converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
+                                                   tp.name.replace(' ', '_'))
+            errvalue = '-1'
+        #
+        elif isinstance(tp, model.PointerType):
+            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
+                                                    tovar, errcode)
+            return
+        #
+        elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
+            # a struct (not a struct pointer) as a function argument
+            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
+                      % (tovar, self._gettypenum(tp), fromvar))
+            self._prnt('    %s;' % errcode)
+            return
+        #
+        elif isinstance(tp, model.FunctionPtrType):
+            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+            errvalue = 'NULL'
+        #
+        else:
+            raise NotImplementedError(tp)
+        #
+        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
+        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
+            tovar, tp.get_c_name(''), errvalue))
+        self._prnt('    %s;' % errcode)
+
+    def _extra_local_variables(self, tp, localvars, freelines):
+        if isinstance(tp, model.PointerType):
+            localvars.add('Py_ssize_t datasize')
+            localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
+            freelines.add('if (large_args_free != NULL)'
+                          ' _cffi_free_array_arguments(large_args_free);')
+
+    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
+        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
+        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
+            self._gettypenum(tp), fromvar, tovar))
+        self._prnt('  if (datasize != 0) {')
+        self._prnt('    %s = ((size_t)datasize) <= 640 ? '
+                   'alloca((size_t)datasize) : NULL;' % (tovar,))
+        self._prnt('    if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
+                   '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
+        self._prnt('            datasize, &large_args_free) < 0)')
+        self._prnt('      %s;' % errcode)
+        self._prnt('  }')
+
+    def _convert_expr_from_c(self, tp, var, context):
+        if isinstance(tp, model.PrimitiveType):
+            if tp.is_integer_type() and tp.name != '_Bool':
+                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
+            elif tp.name != 'long double':
+                return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
+            else:
+                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                    var, self._gettypenum(tp))
+        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.ArrayType):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(model.PointerType(tp.item)))
+        elif isinstance(tp, model.StructOrUnion):
+            if tp.fldnames is None:
+                raise TypeError("'%s' is used as %s, but is opaque" % (
+                    tp._get_c_name(), context))
+            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.EnumType):
+            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        else:
+            raise NotImplementedError(tp)
+
+    # ----------
+    # typedefs: generates no code so far
+
+    _generate_cpy_typedef_collecttype = _generate_nothing
+    _generate_cpy_typedef_decl   = _generate_nothing
+    _generate_cpy_typedef_method = _generate_nothing
+    _loading_cpy_typedef         = _loaded_noop
+    _loaded_cpy_typedef          = _loaded_noop
+
+    # ----------
+    # function declarations
+
+    def _generate_cpy_function_collecttype(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            self._do_collect_type(tp)
+        else:
+            # don't call _do_collect_type(tp) in this common case,
+            # otherwise test_autofilled_struct_as_argument fails
+            for type in tp.args:
+                self._do_collect_type(type)
+            self._do_collect_type(tp.result)
+
+    def _generate_cpy_function_decl(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            # cannot support vararg functions better than this: check for its
+            # exact type (including the fixed arguments), and build it as a
+            # constant function pointer (no CPython wrapper)
+            self._generate_cpy_const(False, name, tp)
+            return
+        prnt = self._prnt
+        numargs = len(tp.args)
+        if numargs == 0:
+            argname = 'noarg'
+        elif numargs == 1:
+            argname = 'arg0'
+        else:
+            argname = 'args'
+        prnt('static PyObject *')
+        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
+        prnt('{')
+        #
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            prnt('  %s;' % type.get_c_name(' x%d' % i, context))
+        #
+        localvars = set()
+        freelines = set()
+        for type in tp.args:
+            self._extra_local_variables(type, localvars, freelines)
+        for decl in sorted(localvars):
+            prnt('  %s;' % (decl,))
+        #
+        if not isinstance(tp.result, model.VoidType):
+            result_code = 'result = '
+            context = 'result of %s' % name
+            prnt('  %s;' % tp.result.get_c_name(' result', context))
+            prnt('  PyObject *pyresult;')
+        else:
+            result_code = ''
+        #
+        if len(tp.args) > 1:
+            rng = range(len(tp.args))
+            for i in rng:
+                prnt('  PyObject *arg%d;' % i)
+            prnt()
+            prnt('  if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
+                'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+            prnt('    return NULL;')
+        prnt()
+        #
+        for i, type in enumerate(tp.args):
+            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
+                                       'return NULL')
+            prnt()
+        #
+        prnt('  Py_BEGIN_ALLOW_THREADS')
+        prnt('  _cffi_restore_errno();')
+        prnt('  { %s%s(%s); }' % (
+            result_code, name,
+            ', '.join(['x%d' % i for i in range(len(tp.args))])))
+        prnt('  _cffi_save_errno();')
+        prnt('  Py_END_ALLOW_THREADS')
+        prnt()
+        #
+        prnt('  (void)self; /* unused */')
+        if numargs == 0:
+            prnt('  (void)noarg; /* unused */')
+        if result_code:
+            prnt('  pyresult = %s;' %
+                 self._convert_expr_from_c(tp.result, 'result', 'result type'))
+            for freeline in freelines:
+                prnt('  ' + freeline)
+            prnt('  return pyresult;')
+        else:
+            for freeline in freelines:
+                prnt('  ' + freeline)
+            prnt('  Py_INCREF(Py_None);')
+            prnt('  return Py_None;')
+        prnt('}')
+        prnt()
+
+    def _generate_cpy_function_method(self, tp, name):
+        if tp.ellipsis:
+            return
+        numargs = len(tp.args)
+        if numargs == 0:
+            meth = 'METH_NOARGS'
+        elif numargs == 1:
+            meth = 'METH_O'
+        else:
+            meth = 'METH_VARARGS'
+        self._prnt('  {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth))
+
+    _loading_cpy_function = _loaded_noop
+
+    def _loaded_cpy_function(self, tp, name, module, library):
+        if tp.ellipsis:
+            return
+        func = getattr(module, name)
+        setattr(library, name, func)
+        self._types_of_builtin_functions[func] = tp
+
+    # ----------
+    # named structs
+
+    _generate_cpy_struct_collecttype = _generate_nothing
+    def _generate_cpy_struct_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'struct', name)
+    def _generate_cpy_struct_method(self, tp, name):
+        self._generate_struct_or_union_method(tp, 'struct', name)
+    def _loading_cpy_struct(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'struct', name, module)
+    def _loaded_cpy_struct(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    _generate_cpy_union_collecttype = _generate_nothing
+    def _generate_cpy_union_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'union', name)
+    def _generate_cpy_union_method(self, tp, name):
+        self._generate_struct_or_union_method(tp, 'union', name)
+    def _loading_cpy_union(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'union', name, module)
+    def _loaded_cpy_union(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    def _generate_struct_or_union_decl(self, tp, prefix, name):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        cname = ('%s %s' % (prefix, name)).strip()
+        #
+        prnt = self._prnt
+        prnt('static void %s(%s *p)' % (checkfuncname, cname))
+        prnt('{')
+        prnt('  /* only to generate compile-time warnings or errors */')
+        prnt('  (void)p;')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if (isinstance(ftype, model.PrimitiveType)
+                and ftype.is_integer_type()) or fbitsize >= 0:
+                # accept all integers, but complain on float or double
+                prnt('  (void)((p->%s) << 1);' % fname)
+            else:
+                # only accept exactly the type declared.
+                try:
+                    prnt('  { %s = &p->%s; (void)tmp; }' % (
+                        ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
+                        fname))
+                except VerificationError as e:
+                    prnt('  /* %s */' % str(e))   # cannot verify it, ignore
+        prnt('}')
+        prnt('static PyObject *')
+        prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
+        prnt('{')
+        prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
+        prnt('  static Py_ssize_t nums[] = {')
+        prnt('    sizeof(%s),' % cname)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if fbitsize >= 0:
+                continue      # xxx ignore fbitsize for now
+            prnt('    offsetof(%s, %s),' % (cname, fname))
+            if isinstance(ftype, model.ArrayType) and ftype.length is None:
+                prnt('    0,  /* %s */' % ftype._get_c_name())
+            else:
+                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  (void)self; /* unused */')
+        prnt('  (void)noarg; /* unused */')
+        prnt('  return _cffi_get_struct_layout(nums);')
+        prnt('  /* the next line is not executed, but compiled */')
+        prnt('  %s(0);' % (checkfuncname,))
+        prnt('}')
+        prnt()
+
+    def _generate_struct_or_union_method(self, tp, prefix, name):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        self._prnt('  {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname,
+                                                         layoutfuncname))
+
+    def _loading_struct_or_union(self, tp, prefix, name, module):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        #
+        function = getattr(module, layoutfuncname)
+        layout = function()
+        if isinstance(tp, model.StructOrUnion) and tp.partial:
+            # use the function()'s sizes and offsets to guide the
+            # layout of the struct
+            totalsize = layout[0]
+            totalalignment = layout[1]
+            fieldofs = layout[2::2]
+            fieldsize = layout[3::2]
+            tp.force_flatten()
+            assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+            tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+        else:
+            cname = ('%s %s' % (prefix, name)).strip()
+            self._struct_pending_verification[tp] = layout, cname
+
+    def _loaded_struct_or_union(self, tp):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered
+
+        if tp in self._struct_pending_verification:
+            # check that the layout sizes and offsets match the real ones
+            def check(realvalue, expectedvalue, msg):
+                if realvalue != expectedvalue:
+                    raise VerificationError(
+                        "%s (we have %d, but C compiler says %d)"
+                        % (msg, expectedvalue, realvalue))
+            ffi = self.ffi
+            BStruct = ffi._get_cached_btype(tp)
+            layout, cname = self._struct_pending_verification.pop(tp)
+            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
+            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
+            i = 2
+            for fname, ftype, fbitsize, fqual in tp.enumfields():
+                if fbitsize >= 0:
+                    continue        # xxx ignore fbitsize for now
+                check(layout[i], ffi.offsetof(BStruct, fname),
+                      "wrong offset for field %r" % (fname,))
+                if layout[i+1] != 0:
+                    BField = ffi._get_cached_btype(ftype)
+                    check(layout[i+1], ffi.sizeof(BField),
+                          "wrong size for field %r" % (fname,))
+                i += 2
+            assert i == len(layout)
+
+    # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    _generate_cpy_anonymous_collecttype = _generate_nothing
+
+    def _generate_cpy_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_decl(tp, name, '')
+        else:
+            self._generate_struct_or_union_decl(tp, '', name)
+
+    def _generate_cpy_anonymous_method(self, tp, name):
+        if not isinstance(tp, model.EnumType):
+            self._generate_struct_or_union_method(tp, '', name)
+
+    def _loading_cpy_anonymous(self, tp, name, module):
+        if isinstance(tp, model.EnumType):
+            self._loading_cpy_enum(tp, name, module)
+        else:
+            self._loading_struct_or_union(tp, '', name, module)
+
+    def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
+        if isinstance(tp, model.EnumType):
+            self._loaded_cpy_enum(tp, name, module, **kwds)
+        else:
+            self._loaded_struct_or_union(tp)
+
+    # ----------
+    # constants, likely declared with '#define'
+
+    def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+                            vartp=None, delayed=True, size_too=False,
+                            check_value=None):
+        prnt = self._prnt
+        funcname = '_cffi_%s_%s' % (category, name)
+        prnt('static int %s(PyObject *lib)' % funcname)
+        prnt('{')
+        prnt('  PyObject *o;')
+        prnt('  int res;')
+        if not is_int:
+            prnt('  %s;' % (vartp or tp).get_c_name(' i', name))
+        else:
+            assert category == 'const'
+        #
+        if check_value is not None:
+            self._check_int_constant_value(name, check_value)
+        #
+        if not is_int:
+            if category == 'var':
+                realexpr = '&' + name
+            else:
+                realexpr = name
+            prnt('  i = (%s);' % (realexpr,))
+            prnt('  o = %s;' % (self._convert_expr_from_c(tp, 'i',
+                                                          'variable type'),))
+            assert delayed
+        else:
+            prnt('  o = _cffi_from_c_int_const(%s);' % name)
+        prnt('  if (o == NULL)')
+        prnt('    return -1;')
+        if size_too:
+            prnt('  {')
+            prnt('    PyObject *o1 = o;')
+            prnt('    o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));'
+                 % (name,))
+            prnt('    Py_DECREF(o1);')
+            prnt('    if (o == NULL)')
+            prnt('      return -1;')
+            prnt('  }')
+        prnt('  res = PyObject_SetAttrString(lib, "%s", o);' % name)
+        prnt('  Py_DECREF(o);')
+        prnt('  if (res < 0)')
+        prnt('    return -1;')
+        prnt('  return %s;' % self._chained_list_constants[delayed])
+        self._chained_list_constants[delayed] = funcname + '(lib)'
+        prnt('}')
+        prnt()
+
+    def _generate_cpy_constant_collecttype(self, tp, name):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        if not is_int:
+            self._do_collect_type(tp)
+
+    def _generate_cpy_constant_decl(self, tp, name):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        self._generate_cpy_const(is_int, name, tp)
+
+    _generate_cpy_constant_method = _generate_nothing
+    _loading_cpy_constant = _loaded_noop
+    _loaded_cpy_constant  = _loaded_noop
+
+    # ----------
+    # enums
+
+    def _check_int_constant_value(self, name, value, err_prefix=''):
+        prnt = self._prnt
+        if value <= 0:
+            prnt('  if ((%s) > 0 || (long)(%s) != %dL) {' % (
+                name, name, value))
+        else:
+            prnt('  if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
+                name, name, value))
+        prnt('    char buf[64];')
+        prnt('    if ((%s) <= 0)' % name)
+        prnt('        snprintf(buf, 63, "%%ld", (long)(%s));' % name)
+        prnt('    else')
+        prnt('        snprintf(buf, 63, "%%lu", (unsigned long)(%s));' %
+             name)
+        prnt('    PyErr_Format(_cffi_VerificationError,')
+        prnt('                 "%s%s has the real value %s, not %s",')
+        prnt('                 "%s", "%s", buf, "%d");' % (
+            err_prefix, name, value))
+        prnt('    return -1;')
+        prnt('  }')
+
+    def _enum_funcname(self, prefix, name):
+        # "$enum_$1" => "___D_enum____D_1"
+        name = name.replace('$', '___D_')
+        return '_cffi_e_%s_%s' % (prefix, name)
+
+    def _generate_cpy_enum_decl(self, tp, name, prefix='enum'):
+        if tp.partial:
+            for enumerator in tp.enumerators:
+                self._generate_cpy_const(True, enumerator, delayed=False)
+            return
+        #
+        funcname = self._enum_funcname(prefix, name)
+        prnt = self._prnt
+        prnt('static int %s(PyObject *lib)' % funcname)
+        prnt('{')
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            self._check_int_constant_value(enumerator, enumvalue,
+                                           "enum %s: " % name)
+        prnt('  return %s;' % self._chained_list_constants[True])
+        self._chained_list_constants[True] = funcname + '(lib)'
+        prnt('}')
+        prnt()
+
+    _generate_cpy_enum_collecttype = _generate_nothing
+    _generate_cpy_enum_method = _generate_nothing
+
+    def _loading_cpy_enum(self, tp, name, module):
+        if tp.partial:
+            enumvalues = [getattr(module, enumerator)
+                          for enumerator in tp.enumerators]
+            tp.enumvalues = tuple(enumvalues)
+            tp.partial_resolved = True
+
+    def _loaded_cpy_enum(self, tp, name, module, library):
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            setattr(library, enumerator, enumvalue)
+
+    # ----------
+    # macros: for now only for integers
+
+    def _generate_cpy_macro_decl(self, tp, name):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        self._generate_cpy_const(True, name, check_value=check_value)
+
+    _generate_cpy_macro_collecttype = _generate_nothing
+    _generate_cpy_macro_method = _generate_nothing
+    _loading_cpy_macro = _loaded_noop
+    _loaded_cpy_macro  = _loaded_noop
+
+    # ----------
+    # global variables
+
+    def _generate_cpy_variable_collecttype(self, tp, name):
+        if isinstance(tp, model.ArrayType):
+            tp_ptr = model.PointerType(tp.item)
+        else:
+            tp_ptr = model.PointerType(tp)
+        self._do_collect_type(tp_ptr)
+
+    def _generate_cpy_variable_decl(self, tp, name):
+        if isinstance(tp, model.ArrayType):
+            tp_ptr = model.PointerType(tp.item)
+            self._generate_cpy_const(False, name, tp, vartp=tp_ptr,
+                                     size_too = tp.length_is_unknown())
+        else:
+            tp_ptr = model.PointerType(tp)
+            self._generate_cpy_const(False, name, tp_ptr, category='var')
+
+    _generate_cpy_variable_method = _generate_nothing
+    _loading_cpy_variable = _loaded_noop
+
+    def _loaded_cpy_variable(self, tp, name, module, library):
+        value = getattr(library, name)
+        if isinstance(tp, model.ArrayType):   # int a[5] is "constant" in the
+                                              # sense that "a=..." is forbidden
+            if tp.length_is_unknown():
+                assert isinstance(value, tuple)
+                (value, size) = value
+                BItemType = self.ffi._get_cached_btype(tp.item)
+                length, rest = divmod(size, self.ffi.sizeof(BItemType))
+                if rest != 0:
+                    raise VerificationError(
+                        "bad size: %r does not seem to be an array of %s" %
+                        (name, tp.item))
+                tp = tp.resolve_length(length)
+            # 'value' is a <cdata 'type *'> which we have to replace with
+            # a <cdata 'type[N]'> if the N is actually known
+            if tp.length is not None:
+                BArray = self.ffi._get_cached_btype(tp)
+                value = self.ffi.cast(BArray, value)
+                setattr(library, name, value)
+            return
+        # remove ptr=<cdata 'int *'> from the library instance, and replace
+        # it by a property on the class, which reads/writes into ptr[0].
+        ptr = value
+        delattr(library, name)
+        def getter(library):
+            return ptr[0]
+        def setter(library, value):
+            ptr[0] = value
+        setattr(type(library), name, property(getter, setter))
+        type(library)._cffi_dir.append(name)
+
+    # ----------
+
+    def _generate_setup_custom(self):
+        prnt = self._prnt
+        prnt('static int _cffi_setup_custom(PyObject *lib)')
+        prnt('{')
+        prnt('  return %s;' % self._chained_list_constants[True])
+        prnt('}')
+
+cffimod_header = r'''
+#include <Python.h>
+#include <stddef.h>
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
+   and cffi/_cffi_include.h */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+#  ifndef __cplusplus
+    typedef unsigned char _Bool;
+#  endif
+# endif
+# define _cffi_float_complex_t   _Fcomplex    /* include <complex.h> for it */
+# define _cffi_double_complex_t  _Dcomplex    /* include <complex.h> for it */
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+# define _cffi_float_complex_t   float _Complex
+# define _cffi_double_complex_t  double _Complex
+#endif
+
+#if PY_MAJOR_VERSION < 3
+# undef PyCapsule_CheckExact
+# undef PyCapsule_GetPointer
+# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
+# define PyCapsule_GetPointer(capsule, name) \
+    (PyCObject_AsVoidPtr(capsule))
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+# define PyInt_FromLong PyLong_FromLong
+#endif
+
+#define _cffi_from_c_double PyFloat_FromDouble
+#define _cffi_from_c_float PyFloat_FromDouble
+#define _cffi_from_c_long PyInt_FromLong
+#define _cffi_from_c_ulong PyLong_FromUnsignedLong
+#define _cffi_from_c_longlong PyLong_FromLongLong
+#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
+#define _cffi_from_c__Bool PyBool_FromLong
+
+#define _cffi_to_c_double PyFloat_AsDouble
+#define _cffi_to_c_float PyFloat_AsDouble
+
+#define _cffi_from_c_int_const(x)                                        \
+    (((x) > 0) ?                                                         \
+        ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ?      \
+            PyInt_FromLong((long)(x)) :                                  \
+            PyLong_FromUnsignedLongLong((unsigned long long)(x)) :       \
+        ((long long)(x) >= (long long)LONG_MIN) ?                        \
+            PyInt_FromLong((long)(x)) :                                  \
+            PyLong_FromLongLong((long long)(x)))
+
+#define _cffi_from_c_int(x, type)                                        \
+    (((type)-1) > 0 ? /* unsigned */                                     \
+        (sizeof(type) < sizeof(long) ?                                   \
+            PyInt_FromLong((long)x) :                                    \
+         sizeof(type) == sizeof(long) ?                                  \
+            PyLong_FromUnsignedLong((unsigned long)x) :                  \
+            PyLong_FromUnsignedLongLong((unsigned long long)x)) :        \
+        (sizeof(type) <= sizeof(long) ?                                  \
+            PyInt_FromLong((long)x) :                                    \
+            PyLong_FromLongLong((long long)x)))
+
+#define _cffi_to_c_int(o, type)                                          \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+                                         : (type)_cffi_to_c_i8(o)) :     \
+     sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
+                                         : (type)_cffi_to_c_i16(o)) :    \
+     sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o)       \
+                                         : (type)_cffi_to_c_i32(o)) :    \
+     sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
+                                         : (type)_cffi_to_c_i64(o)) :    \
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
+
+#define _cffi_to_c_i8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[1])
+#define _cffi_to_c_u8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[2])
+#define _cffi_to_c_i16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[3])
+#define _cffi_to_c_u16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[4])
+#define _cffi_to_c_i32                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[5])
+#define _cffi_to_c_u32                                                   \
+                 ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#define _cffi_to_c_i64                                                   \
+                 ((long long(*)(PyObject *))_cffi_exports[7])
+#define _cffi_to_c_u64                                                   \
+                 ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+#define _cffi_to_c_char                                                  \
+                 ((int(*)(PyObject *))_cffi_exports[9])
+#define _cffi_from_c_pointer                                             \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
+#define _cffi_to_c_pointer                                               \
+    ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
+#define _cffi_get_struct_layout                                          \
+    ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
+#define _cffi_restore_errno                                              \
+    ((void(*)(void))_cffi_exports[13])
+#define _cffi_save_errno                                                 \
+    ((void(*)(void))_cffi_exports[14])
+#define _cffi_from_c_char                                                \
+    ((PyObject *(*)(char))_cffi_exports[15])
+#define _cffi_from_c_deref                                               \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
+#define _cffi_to_c                                                       \
+    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
+#define _cffi_from_c_struct                                              \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
+#define _cffi_to_c_wchar_t                                               \
+    ((wchar_t(*)(PyObject *))_cffi_exports[19])
+#define _cffi_from_c_wchar_t                                             \
+    ((PyObject *(*)(wchar_t))_cffi_exports[20])
+#define _cffi_to_c_long_double                                           \
+    ((long double(*)(PyObject *))_cffi_exports[21])
+#define _cffi_to_c__Bool                                                 \
+    ((_Bool(*)(PyObject *))_cffi_exports[22])
+#define _cffi_prepare_pointer_call_argument                              \
+    ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
+#define _cffi_convert_array_from_object                                  \
+    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
+#define _CFFI_NUM_EXPORTS 25
+
+typedef struct _ctypedescr CTypeDescrObject;
+
+static void *_cffi_exports[_CFFI_NUM_EXPORTS];
+static PyObject *_cffi_types, *_cffi_VerificationError;
+
+static int _cffi_setup_custom(PyObject *lib);   /* forward */
+
+static PyObject *_cffi_setup(PyObject *self, PyObject *args)
+{
+    PyObject *library;
+    int was_alive = (_cffi_types != NULL);
+    (void)self; /* unused */
+    if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
+                                       &library))
+        return NULL;
+    Py_INCREF(_cffi_types);
+    Py_INCREF(_cffi_VerificationError);
+    if (_cffi_setup_custom(library) < 0)
+        return NULL;
+    return PyBool_FromLong(was_alive);
+}
+
+union _cffi_union_alignment_u {
+    unsigned char m_char;
+    unsigned short m_short;
+    unsigned int m_int;
+    unsigned long m_long;
+    unsigned long long m_longlong;
+    float m_float;
+    double m_double;
+    long double m_longdouble;
+};
+
+struct _cffi_freeme_s {
+    struct _cffi_freeme_s *next;
+    union _cffi_union_alignment_u alignment;
+};
+
+#ifdef __GNUC__
+  __attribute__((unused))
+#endif
+static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg,
+                                        char **output_data, Py_ssize_t datasize,
+                                        struct _cffi_freeme_s **freeme)
+{
+    char *p;
+    if (datasize < 0)
+        return -1;
+
+    p = *output_data;
+    if (p == NULL) {
+        struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
+            offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
+        if (fp == NULL)
+            return -1;
+        fp->next = *freeme;
+        *freeme = fp;
+        p = *output_data = (char *)&fp->alignment;
+    }
+    memset((void *)p, 0, (size_t)datasize);
+    return _cffi_convert_array_from_object(p, ctptr, arg);
+}
+
+#ifdef __GNUC__
+  __attribute__((unused))
+#endif
+static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
+{
+    do {
+        void *p = (void *)freeme;
+        freeme = freeme->next;
+        PyObject_Free(p);
+    } while (freeme != NULL);
+}
+
+static int _cffi_init(void)
+{
+    PyObject *module, *c_api_object = NULL;
+
+    module = PyImport_ImportModule("_cffi_backend");
+    if (module == NULL)
+        goto failure;
+
+    c_api_object = PyObject_GetAttrString(module, "_C_API");
+    if (c_api_object == NULL)
+        goto failure;
+    if (!PyCapsule_CheckExact(c_api_object)) {
+        PyErr_SetNone(PyExc_ImportError);
+        goto failure;
+    }
+    memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
+           _CFFI_NUM_EXPORTS * sizeof(void *));
+
+    Py_DECREF(module);
+    Py_DECREF(c_api_object);
+    return 0;
+
+  failure:
+    Py_XDECREF(module);
+    Py_XDECREF(c_api_object);
+    return -1;
+}
+
+#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
+
+/**********/
+'''
diff --git a/.venv/lib/python3.8/site-packages/cffi/vengine_gen.py b/.venv/lib/python3.8/site-packages/cffi/vengine_gen.py
new file mode 100644
index 0000000000000000000000000000000000000000..bffc82122c353fbd15a395d77e829a95bf8546b0
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/vengine_gen.py
@@ -0,0 +1,679 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
+import sys, os
+import types
+
+from . import model
+from .error import VerificationError
+
+
+class VGenericEngine(object):
+    _class_key = 'g'
+    _gen_python_module = False
+
+    def __init__(self, verifier):
+        self.verifier = verifier
+        self.ffi = verifier.ffi
+        self.export_symbols = []
+        self._struct_pending_verification = {}
+
+    def patch_extension_kwds(self, kwds):
+        # add 'export_symbols' to the dictionary.  Note that we add the
+        # list before filling it.  When we fill it, it will thus also show
+        # up in kwds['export_symbols'].
+        kwds.setdefault('export_symbols', self.export_symbols)
+
+    def find_module(self, module_name, path, so_suffixes):
+        for so_suffix in so_suffixes:
+            basename = module_name + so_suffix
+            if path is None:
+                path = sys.path
+            for dirname in path:
+                filename = os.path.join(dirname, basename)
+                if os.path.isfile(filename):
+                    return filename
+
+    def collect_types(self):
+        pass      # not needed in the generic engine
+
+    def _prnt(self, what=''):
+        self._f.write(what + '\n')
+
+    def write_source_to_f(self):
+        prnt = self._prnt
+        # first paste some standard set of lines that are mostly '#include'
+        prnt(cffimod_header)
+        # then paste the C source given by the user, verbatim.
+        prnt(self.verifier.preamble)
+        #
+        # call generate_gen_xxx_decl(), for every xxx found from
+        # ffi._parser._declarations.  This generates all the functions.
+        self._generate('decl')
+        #
+        # on Windows, distutils insists on putting init_cffi_xyz in
+        # 'export_symbols', so instead of fighting it, just give up and
+        # give it one
+        if sys.platform == 'win32':
+            if sys.version_info >= (3,):
+                prefix = 'PyInit_'
+            else:
+                prefix = 'init'
+            modname = self.verifier.get_module_name()
+            prnt("void %s%s(void) { }\n" % (prefix, modname))
+
+    def load_library(self, flags=0):
+        # import it with the CFFI backend
+        backend = self.ffi._backend
+        # needs to make a path that contains '/', on Posix
+        filename = os.path.join(os.curdir, self.verifier.modulefilename)
+        module = backend.load_library(filename, flags)
+        #
+        # call loading_gen_struct() to get the struct layout inferred by
+        # the C compiler
+        self._load(module, 'loading')
+
+        # build the FFILibrary class and instance, this is a module subclass
+        # because modules are expected to have usually-constant-attributes and
+        # in PyPy this means the JIT is able to treat attributes as constant,
+        # which we want.
+        class FFILibrary(types.ModuleType):
+            _cffi_generic_module = module
+            _cffi_ffi = self.ffi
+            _cffi_dir = []
+            def __dir__(self):
+                return FFILibrary._cffi_dir
+        library = FFILibrary("")
+        #
+        # finally, call the loaded_gen_xxx() functions.  This will set
+        # up the 'library' object.
+        self._load(module, 'loaded', library=library)
+        return library
+
+    def _get_declarations(self):
+        lst = [(key, tp) for (key, (tp, qual)) in
+                                self.ffi._parser._declarations.items()]
+        lst.sort()
+        return lst
+
+    def _generate(self, step_name):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            try:
+                method = getattr(self, '_generate_gen_%s_%s' % (kind,
+                                                                step_name))
+            except AttributeError:
+                raise VerificationError(
+                    "not implemented in verify(): %r" % name)
+            try:
+                method(tp, realname)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _load(self, module, step_name, **kwds):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            method = getattr(self, '_%s_gen_%s' % (step_name, kind))
+            try:
+                method(tp, realname, module, **kwds)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _generate_nothing(self, tp, name):
+        pass
+
+    def _loaded_noop(self, tp, name, module, **kwds):
+        pass
+
+    # ----------
+    # typedefs: generates no code so far
+
+    _generate_gen_typedef_decl   = _generate_nothing
+    _loading_gen_typedef         = _loaded_noop
+    _loaded_gen_typedef          = _loaded_noop
+
+    # ----------
+    # function declarations
+
+    def _generate_gen_function_decl(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            # cannot support vararg functions better than this: check for its
+            # exact type (including the fixed arguments), and build it as a
+            # constant function pointer (no _cffi_f_%s wrapper)
+            self._generate_gen_const(False, name, tp)
+            return
+        prnt = self._prnt
+        numargs = len(tp.args)
+        argnames = []
+        for i, type in enumerate(tp.args):
+            indirection = ''
+            if isinstance(type, model.StructOrUnion):
+                indirection = '*'
+            argnames.append('%sx%d' % (indirection, i))
+        context = 'argument of %s' % name
+        arglist = [type.get_c_name(' %s' % arg, context)
+                   for type, arg in zip(tp.args, argnames)]
+        tpresult = tp.result
+        if isinstance(tpresult, model.StructOrUnion):
+            arglist.insert(0, tpresult.get_c_name(' *r', context))
+            tpresult = model.void_type
+        arglist = ', '.join(arglist) or 'void'
+        wrappername = '_cffi_f_%s' % name
+        self.export_symbols.append(wrappername)
+        if tp.abi:
+            abi = tp.abi + ' '
+        else:
+            abi = ''
+        funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist)
+        context = 'result of %s' % name
+        prnt(tpresult.get_c_name(funcdecl, context))
+        prnt('{')
+        #
+        if isinstance(tp.result, model.StructOrUnion):
+            result_code = '*r = '
+        elif not isinstance(tp.result, model.VoidType):
+            result_code = 'return '
+        else:
+            result_code = ''
+        prnt('  %s%s(%s);' % (result_code, name, ', '.join(argnames)))
+        prnt('}')
+        prnt()
+
+    _loading_gen_function = _loaded_noop
+
+    def _loaded_gen_function(self, tp, name, module, library):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            newfunction = self._load_constant(False, tp, name, module)
+        else:
+            indirections = []
+            base_tp = tp
+            if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args)
+                    or isinstance(tp.result, model.StructOrUnion)):
+                indirect_args = []
+                for i, typ in enumerate(tp.args):
+                    if isinstance(typ, model.StructOrUnion):
+                        typ = model.PointerType(typ)
+                        indirections.append((i, typ))
+                    indirect_args.append(typ)
+                indirect_result = tp.result
+                if isinstance(indirect_result, model.StructOrUnion):
+                    if indirect_result.fldtypes is None:
+                        raise TypeError("'%s' is used as result type, "
+                                        "but is opaque" % (
+                                            indirect_result._get_c_name(),))
+                    indirect_result = model.PointerType(indirect_result)
+                    indirect_args.insert(0, indirect_result)
+                    indirections.insert(0, ("result", indirect_result))
+                    indirect_result = model.void_type
+                tp = model.FunctionPtrType(tuple(indirect_args),
+                                           indirect_result, tp.ellipsis)
+            BFunc = self.ffi._get_cached_btype(tp)
+            wrappername = '_cffi_f_%s' % name
+            newfunction = module.load_function(BFunc, wrappername)
+            for i, typ in indirections:
+                newfunction = self._make_struct_wrapper(newfunction, i, typ,
+                                                        base_tp)
+        setattr(library, name, newfunction)
+        type(library)._cffi_dir.append(name)
+
+    def _make_struct_wrapper(self, oldfunc, i, tp, base_tp):
+        backend = self.ffi._backend
+        BType = self.ffi._get_cached_btype(tp)
+        if i == "result":
+            ffi = self.ffi
+            def newfunc(*args):
+                res = ffi.new(BType)
+                oldfunc(res, *args)
+                return res[0]
+        else:
+            def newfunc(*args):
+                args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
+                return oldfunc(*args)
+        newfunc._cffi_base_type = base_tp
+        return newfunc
+
+    # ----------
+    # named structs
+
+    def _generate_gen_struct_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'struct', name)
+
+    def _loading_gen_struct(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'struct', name, module)
+
+    def _loaded_gen_struct(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    def _generate_gen_union_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'union', name)
+
+    def _loading_gen_union(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'union', name, module)
+
+    def _loaded_gen_union(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    def _generate_struct_or_union_decl(self, tp, prefix, name):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        cname = ('%s %s' % (prefix, name)).strip()
+        #
+        prnt = self._prnt
+        prnt('static void %s(%s *p)' % (checkfuncname, cname))
+        prnt('{')
+        prnt('  /* only to generate compile-time warnings or errors */')
+        prnt('  (void)p;')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if (isinstance(ftype, model.PrimitiveType)
+                and ftype.is_integer_type()) or fbitsize >= 0:
+                # accept all integers, but complain on float or double
+                prnt('  (void)((p->%s) << 1);' % fname)
+            else:
+                # only accept exactly the type declared.
+                try:
+                    prnt('  { %s = &p->%s; (void)tmp; }' % (
+                        ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
+                        fname))
+                except VerificationError as e:
+                    prnt('  /* %s */' % str(e))   # cannot verify it, ignore
+        prnt('}')
+        self.export_symbols.append(layoutfuncname)
+        prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,))
+        prnt('{')
+        prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
+        prnt('  static intptr_t nums[] = {')
+        prnt('    sizeof(%s),' % cname)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if fbitsize >= 0:
+                continue      # xxx ignore fbitsize for now
+            prnt('    offsetof(%s, %s),' % (cname, fname))
+            if isinstance(ftype, model.ArrayType) and ftype.length is None:
+                prnt('    0,  /* %s */' % ftype._get_c_name())
+            else:
+                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  return nums[i];')
+        prnt('  /* the next line is not executed, but compiled */')
+        prnt('  %s(0);' % (checkfuncname,))
+        prnt('}')
+        prnt()
+
+    def _loading_struct_or_union(self, tp, prefix, name, module):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        #
+        BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0]
+        function = module.load_function(BFunc, layoutfuncname)
+        layout = []
+        num = 0
+        while True:
+            x = function(num)
+            if x < 0: break
+            layout.append(x)
+            num += 1
+        if isinstance(tp, model.StructOrUnion) and tp.partial:
+            # use the function()'s sizes and offsets to guide the
+            # layout of the struct
+            totalsize = layout[0]
+            totalalignment = layout[1]
+            fieldofs = layout[2::2]
+            fieldsize = layout[3::2]
+            tp.force_flatten()
+            assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+            tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+        else:
+            cname = ('%s %s' % (prefix, name)).strip()
+            self._struct_pending_verification[tp] = layout, cname
+
+    def _loaded_struct_or_union(self, tp):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered
+
+        if tp in self._struct_pending_verification:
+            # check that the layout sizes and offsets match the real ones
+            def check(realvalue, expectedvalue, msg):
+                if realvalue != expectedvalue:
+                    raise VerificationError(
+                        "%s (we have %d, but C compiler says %d)"
+                        % (msg, expectedvalue, realvalue))
+            ffi = self.ffi
+            BStruct = ffi._get_cached_btype(tp)
+            layout, cname = self._struct_pending_verification.pop(tp)
+            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
+            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
+            i = 2
+            for fname, ftype, fbitsize, fqual in tp.enumfields():
+                if fbitsize >= 0:
+                    continue        # xxx ignore fbitsize for now
+                check(layout[i], ffi.offsetof(BStruct, fname),
+                      "wrong offset for field %r" % (fname,))
+                if layout[i+1] != 0:
+                    BField = ffi._get_cached_btype(ftype)
+                    check(layout[i+1], ffi.sizeof(BField),
+                          "wrong size for field %r" % (fname,))
+                i += 2
+            assert i == len(layout)
+
+    # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    def _generate_gen_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_gen_enum_decl(tp, name, '')
+        else:
+            self._generate_struct_or_union_decl(tp, '', name)
+
+    def _loading_gen_anonymous(self, tp, name, module):
+        if isinstance(tp, model.EnumType):
+            self._loading_gen_enum(tp, name, module, '')
+        else:
+            self._loading_struct_or_union(tp, '', name, module)
+
+    def _loaded_gen_anonymous(self, tp, name, module, **kwds):
+        if isinstance(tp, model.EnumType):
+            self._loaded_gen_enum(tp, name, module, **kwds)
+        else:
+            self._loaded_struct_or_union(tp)
+
+    # ----------
+    # constants, likely declared with '#define'
+
+    def _generate_gen_const(self, is_int, name, tp=None, category='const',
+                            check_value=None):
+        prnt = self._prnt
+        funcname = '_cffi_%s_%s' % (category, name)
+        self.export_symbols.append(funcname)
+        if check_value is not None:
+            assert is_int
+            assert category == 'const'
+            prnt('int %s(char *out_error)' % funcname)
+            prnt('{')
+            self._check_int_constant_value(name, check_value)
+            prnt('  return 0;')
+            prnt('}')
+        elif is_int:
+            assert category == 'const'
+            prnt('int %s(long long *out_value)' % funcname)
+            prnt('{')
+            prnt('  *out_value = (long long)(%s);' % (name,))
+            prnt('  return (%s) <= 0;' % (name,))
+            prnt('}')
+        else:
+            assert tp is not None
+            assert check_value is None
+            if category == 'var':
+                ampersand = '&'
+            else:
+                ampersand = ''
+            extra = ''
+            if category == 'const' and isinstance(tp, model.StructOrUnion):
+                extra = 'const *'
+                ampersand = '&'
+            prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name))
+            prnt('{')
+            prnt('  return (%s%s);' % (ampersand, name))
+            prnt('}')
+        prnt()
+
+    def _generate_gen_constant_decl(self, tp, name):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        self._generate_gen_const(is_int, name, tp)
+
+    _loading_gen_constant = _loaded_noop
+
+    def _load_constant(self, is_int, tp, name, module, check_value=None):
+        funcname = '_cffi_const_%s' % name
+        if check_value is not None:
+            assert is_int
+            self._load_known_int_constant(module, funcname)
+            value = check_value
+        elif is_int:
+            BType = self.ffi._typeof_locked("long long*")[0]
+            BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0]
+            function = module.load_function(BFunc, funcname)
+            p = self.ffi.new(BType)
+            negative = function(p)
+            value = int(p[0])
+            if value < 0 and not negative:
+                BLongLong = self.ffi._typeof_locked("long long")[0]
+                value += (1 << (8*self.ffi.sizeof(BLongLong)))
+        else:
+            assert check_value is None
+            fntypeextra = '(*)(void)'
+            if isinstance(tp, model.StructOrUnion):
+                fntypeextra = '*' + fntypeextra
+            BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0]
+            function = module.load_function(BFunc, funcname)
+            value = function()
+            if isinstance(tp, model.StructOrUnion):
+                value = value[0]
+        return value
+
+    def _loaded_gen_constant(self, tp, name, module, library):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        value = self._load_constant(is_int, tp, name, module)
+        setattr(library, name, value)
+        type(library)._cffi_dir.append(name)
+
+    # ----------
+    # enums
+
+    def _check_int_constant_value(self, name, value):
+        prnt = self._prnt
+        if value <= 0:
+            prnt('  if ((%s) > 0 || (long)(%s) != %dL) {' % (
+                name, name, value))
+        else:
+            prnt('  if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
+                name, name, value))
+        prnt('    char buf[64];')
+        prnt('    if ((%s) <= 0)' % name)
+        prnt('        sprintf(buf, "%%ld", (long)(%s));' % name)
+        prnt('    else')
+        prnt('        sprintf(buf, "%%lu", (unsigned long)(%s));' %
+             name)
+        prnt('    sprintf(out_error, "%s has the real value %s, not %s",')
+        prnt('            "%s", buf, "%d");' % (name[:100], value))
+        prnt('    return -1;')
+        prnt('  }')
+
+    def _load_known_int_constant(self, module, funcname):
+        BType = self.ffi._typeof_locked("char[]")[0]
+        BFunc = self.ffi._typeof_locked("int(*)(char*)")[0]
+        function = module.load_function(BFunc, funcname)
+        p = self.ffi.new(BType, 256)
+        if function(p) < 0:
+            error = self.ffi.string(p)
+            if sys.version_info >= (3,):
+                error = str(error, 'utf-8')
+            raise VerificationError(error)
+
+    def _enum_funcname(self, prefix, name):
+        # "$enum_$1" => "___D_enum____D_1"
+        name = name.replace('$', '___D_')
+        return '_cffi_e_%s_%s' % (prefix, name)
+
+    def _generate_gen_enum_decl(self, tp, name, prefix='enum'):
+        if tp.partial:
+            for enumerator in tp.enumerators:
+                self._generate_gen_const(True, enumerator)
+            return
+        #
+        funcname = self._enum_funcname(prefix, name)
+        self.export_symbols.append(funcname)
+        prnt = self._prnt
+        prnt('int %s(char *out_error)' % funcname)
+        prnt('{')
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            self._check_int_constant_value(enumerator, enumvalue)
+        prnt('  return 0;')
+        prnt('}')
+        prnt()
+
+    def _loading_gen_enum(self, tp, name, module, prefix='enum'):
+        if tp.partial:
+            enumvalues = [self._load_constant(True, tp, enumerator, module)
+                          for enumerator in tp.enumerators]
+            tp.enumvalues = tuple(enumvalues)
+            tp.partial_resolved = True
+        else:
+            funcname = self._enum_funcname(prefix, name)
+            self._load_known_int_constant(module, funcname)
+
+    def _loaded_gen_enum(self, tp, name, module, library):
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            setattr(library, enumerator, enumvalue)
+            type(library)._cffi_dir.append(enumerator)
+
+    # ----------
+    # macros: for now only for integers
+
+    def _generate_gen_macro_decl(self, tp, name):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        self._generate_gen_const(True, name, check_value=check_value)
+
+    _loading_gen_macro = _loaded_noop
+
+    def _loaded_gen_macro(self, tp, name, module, library):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        value = self._load_constant(True, tp, name, module,
+                                    check_value=check_value)
+        setattr(library, name, value)
+        type(library)._cffi_dir.append(name)
+
+    # ----------
+    # global variables
+
+    def _generate_gen_variable_decl(self, tp, name):
+        if isinstance(tp, model.ArrayType):
+            if tp.length_is_unknown():
+                prnt = self._prnt
+                funcname = '_cffi_sizeof_%s' % (name,)
+                self.export_symbols.append(funcname)
+                prnt("size_t %s(void)" % funcname)
+                prnt("{")
+                prnt("  return sizeof(%s);" % (name,))
+                prnt("}")
+            tp_ptr = model.PointerType(tp.item)
+            self._generate_gen_const(False, name, tp_ptr)
+        else:
+            tp_ptr = model.PointerType(tp)
+            self._generate_gen_const(False, name, tp_ptr, category='var')
+
+    _loading_gen_variable = _loaded_noop
+
+    def _loaded_gen_variable(self, tp, name, module, library):
+        if isinstance(tp, model.ArrayType):   # int a[5] is "constant" in the
+                                              # sense that "a=..." is forbidden
+            if tp.length_is_unknown():
+                funcname = '_cffi_sizeof_%s' % (name,)
+                BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0]
+                function = module.load_function(BFunc, funcname)
+                size = function()
+                BItemType = self.ffi._get_cached_btype(tp.item)
+                length, rest = divmod(size, self.ffi.sizeof(BItemType))
+                if rest != 0:
+                    raise VerificationError(
+                        "bad size: %r does not seem to be an array of %s" %
+                        (name, tp.item))
+                tp = tp.resolve_length(length)
+            tp_ptr = model.PointerType(tp.item)
+            value = self._load_constant(False, tp_ptr, name, module)
+            # 'value' is a <cdata 'type *'> which we have to replace with
+            # a <cdata 'type[N]'> if the N is actually known
+            if tp.length is not None:
+                BArray = self.ffi._get_cached_btype(tp)
+                value = self.ffi.cast(BArray, value)
+            setattr(library, name, value)
+            type(library)._cffi_dir.append(name)
+            return
+        # remove ptr=<cdata 'int *'> from the library instance, and replace
+        # it by a property on the class, which reads/writes into ptr[0].
+        funcname = '_cffi_var_%s' % name
+        BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0]
+        function = module.load_function(BFunc, funcname)
+        ptr = function()
+        def getter(library):
+            return ptr[0]
+        def setter(library, value):
+            ptr[0] = value
+        setattr(type(library), name, property(getter, setter))
+        type(library)._cffi_dir.append(name)
+
+cffimod_header = r'''
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>   /* XXX for ssize_t on some platforms */
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
+   and cffi/_cffi_include.h */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+#  ifndef __cplusplus
+    typedef unsigned char _Bool;
+#  endif
+# endif
+# define _cffi_float_complex_t   _Fcomplex    /* include <complex.h> for it */
+# define _cffi_double_complex_t  _Dcomplex    /* include <complex.h> for it */
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+# define _cffi_float_complex_t   float _Complex
+# define _cffi_double_complex_t  double _Complex
+#endif
+'''
diff --git a/.venv/lib/python3.8/site-packages/cffi/verifier.py b/.venv/lib/python3.8/site-packages/cffi/verifier.py
new file mode 100644
index 0000000000000000000000000000000000000000..e392a2b7fdab66662f5a32885cbe865d6c538ebe
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cffi/verifier.py
@@ -0,0 +1,306 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
+import sys, os, binascii, shutil, io
+from . import __version_verifier_modules__
+from . import ffiplatform
+from .error import VerificationError
+
+if sys.version_info >= (3, 3):
+    import importlib.machinery
+    def _extension_suffixes():
+        return importlib.machinery.EXTENSION_SUFFIXES[:]
+else:
+    import imp
+    def _extension_suffixes():
+        return [suffix for suffix, _, type in imp.get_suffixes()
+                if type == imp.C_EXTENSION]
+
+
+if sys.version_info >= (3,):
+    NativeIO = io.StringIO
+else:
+    class NativeIO(io.BytesIO):
+        def write(self, s):
+            if isinstance(s, unicode):
+                s = s.encode('ascii')
+            super(NativeIO, self).write(s)
+
+
+class Verifier(object):
+
+    def __init__(self, ffi, preamble, tmpdir=None, modulename=None,
+                 ext_package=None, tag='', force_generic_engine=False,
+                 source_extension='.c', flags=None, relative_to=None, **kwds):
+        if ffi._parser._uses_new_feature:
+            raise VerificationError(
+                "feature not supported with ffi.verify(), but only "
+                "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))
+        self.ffi = ffi
+        self.preamble = preamble
+        if not modulename:
+            flattened_kwds = ffiplatform.flatten(kwds)
+        vengine_class = _locate_engine_class(ffi, force_generic_engine)
+        self._vengine = vengine_class(self)
+        self._vengine.patch_extension_kwds(kwds)
+        self.flags = flags
+        self.kwds = self.make_relative_to(kwds, relative_to)
+        #
+        if modulename:
+            if tag:
+                raise TypeError("can't specify both 'modulename' and 'tag'")
+        else:
+            key = '\x00'.join(['%d.%d' % sys.version_info[:2],
+                               __version_verifier_modules__,
+                               preamble, flattened_kwds] +
+                              ffi._cdefsources)
+            if sys.version_info >= (3,):
+                key = key.encode('utf-8')
+            k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+            k1 = k1.lstrip('0x').rstrip('L')
+            k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+            k2 = k2.lstrip('0').rstrip('L')
+            modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
+                                              k1, k2)
+        suffix = _get_so_suffixes()[0]
+        self.tmpdir = tmpdir or _caller_dir_pycache()
+        self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension)
+        self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)
+        self.ext_package = ext_package
+        self._has_source = False
+        self._has_module = False
+
+    def write_source(self, file=None):
+        """Write the C source code.  It is produced in 'self.sourcefilename',
+        which can be tweaked beforehand."""
+        with self.ffi._lock:
+            if self._has_source and file is None:
+                raise VerificationError(
+                    "source code already written")
+            self._write_source(file)
+
+    def compile_module(self):
+        """Write the C source code (if not done already) and compile it.
+        This produces a dynamic link library in 'self.modulefilename'."""
+        with self.ffi._lock:
+            if self._has_module:
+                raise VerificationError("module already compiled")
+            if not self._has_source:
+                self._write_source()
+            self._compile_module()
+
+    def load_library(self):
+        """Get a C module from this Verifier instance.
+        Returns an instance of a FFILibrary class that behaves like the
+        objects returned by ffi.dlopen(), but that delegates all
+        operations to the C module.  If necessary, the C code is written
+        and compiled first.
+        """
+        with self.ffi._lock:
+            if not self._has_module:
+                self._locate_module()
+                if not self._has_module:
+                    if not self._has_source:
+                        self._write_source()
+                    self._compile_module()
+            return self._load_library()
+
+    def get_module_name(self):
+        basename = os.path.basename(self.modulefilename)
+        # kill both the .so extension and the other .'s, as introduced
+        # by Python 3: 'basename.cpython-33m.so'
+        basename = basename.split('.', 1)[0]
+        # and the _d added in Python 2 debug builds --- but try to be
+        # conservative and not kill a legitimate _d
+        if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
+            basename = basename[:-2]
+        return basename
+
+    def get_extension(self):
+        if not self._has_source:
+            with self.ffi._lock:
+                if not self._has_source:
+                    self._write_source()
+        sourcename = ffiplatform.maybe_relative_path(self.sourcefilename)
+        modname = self.get_module_name()
+        return ffiplatform.get_extension(sourcename, modname, **self.kwds)
+
+    def generates_python_module(self):
+        return self._vengine._gen_python_module
+
+    def make_relative_to(self, kwds, relative_to):
+        if relative_to and os.path.dirname(relative_to):
+            dirname = os.path.dirname(relative_to)
+            kwds = kwds.copy()
+            for key in ffiplatform.LIST_OF_FILE_NAMES:
+                if key in kwds:
+                    lst = kwds[key]
+                    if not isinstance(lst, (list, tuple)):
+                        raise TypeError("keyword '%s' should be a list or tuple"
+                                        % (key,))
+                    lst = [os.path.join(dirname, fn) for fn in lst]
+                    kwds[key] = lst
+        return kwds
+
+    # ----------
+
+    def _locate_module(self):
+        if not os.path.isfile(self.modulefilename):
+            if self.ext_package:
+                try:
+                    pkg = __import__(self.ext_package, None, None, ['__doc__'])
+                except ImportError:
+                    return      # cannot import the package itself, give up
+                    # (e.g. it might be called differently before installation)
+                path = pkg.__path__
+            else:
+                path = None
+            filename = self._vengine.find_module(self.get_module_name(), path,
+                                                 _get_so_suffixes())
+            if filename is None:
+                return
+            self.modulefilename = filename
+        self._vengine.collect_types()
+        self._has_module = True
+
+    def _write_source_to(self, file):
+        self._vengine._f = file
+        try:
+            self._vengine.write_source_to_f()
+        finally:
+            del self._vengine._f
+
+    def _write_source(self, file=None):
+        if file is not None:
+            self._write_source_to(file)
+        else:
+            # Write our source file to an in memory file.
+            f = NativeIO()
+            self._write_source_to(f)
+            source_data = f.getvalue()
+
+            # Determine if this matches the current file
+            if os.path.exists(self.sourcefilename):
+                with open(self.sourcefilename, "r") as fp:
+                    needs_written = not (fp.read() == source_data)
+            else:
+                needs_written = True
+
+            # Actually write the file out if it doesn't match
+            if needs_written:
+                _ensure_dir(self.sourcefilename)
+                with open(self.sourcefilename, "w") as fp:
+                    fp.write(source_data)
+
+            # Set this flag
+            self._has_source = True
+
+    def _compile_module(self):
+        # compile this C source
+        tmpdir = os.path.dirname(self.sourcefilename)
+        outputfilename = ffiplatform.compile(tmpdir, self.get_extension())
+        try:
+            same = ffiplatform.samefile(outputfilename, self.modulefilename)
+        except OSError:
+            same = False
+        if not same:
+            _ensure_dir(self.modulefilename)
+            shutil.move(outputfilename, self.modulefilename)
+        self._has_module = True
+
+    def _load_library(self):
+        assert self._has_module
+        if self.flags is not None:
+            return self._vengine.load_library(self.flags)
+        else:
+            return self._vengine.load_library()
+
+# ____________________________________________________________
+
+_FORCE_GENERIC_ENGINE = False      # for tests
+
+def _locate_engine_class(ffi, force_generic_engine):
+    if _FORCE_GENERIC_ENGINE:
+        force_generic_engine = True
+    if not force_generic_engine:
+        if '__pypy__' in sys.builtin_module_names:
+            force_generic_engine = True
+        else:
+            try:
+                import _cffi_backend
+            except ImportError:
+                _cffi_backend = '?'
+            if ffi._backend is not _cffi_backend:
+                force_generic_engine = True
+    if force_generic_engine:
+        from . import vengine_gen
+        return vengine_gen.VGenericEngine
+    else:
+        from . import vengine_cpy
+        return vengine_cpy.VCPythonEngine
+
+# ____________________________________________________________
+
+_TMPDIR = None
+
+def _caller_dir_pycache():
+    if _TMPDIR:
+        return _TMPDIR
+    result = os.environ.get('CFFI_TMPDIR')
+    if result:
+        return result
+    filename = sys._getframe(2).f_code.co_filename
+    return os.path.abspath(os.path.join(os.path.dirname(filename),
+                           '__pycache__'))
+
+def set_tmpdir(dirname):
+    """Set the temporary directory to use instead of __pycache__."""
+    global _TMPDIR
+    _TMPDIR = dirname
+
+def cleanup_tmpdir(tmpdir=None, keep_so=False):
+    """Clean up the temporary directory by removing all files in it
+    called `_cffi_*.{c,so}` as well as the `build` subdirectory."""
+    tmpdir = tmpdir or _caller_dir_pycache()
+    try:
+        filelist = os.listdir(tmpdir)
+    except OSError:
+        return
+    if keep_so:
+        suffix = '.c'   # only remove .c files
+    else:
+        suffix = _get_so_suffixes()[0].lower()
+    for fn in filelist:
+        if fn.lower().startswith('_cffi_') and (
+                fn.lower().endswith(suffix) or fn.lower().endswith('.c')):
+            try:
+                os.unlink(os.path.join(tmpdir, fn))
+            except OSError:
+                pass
+    clean_dir = [os.path.join(tmpdir, 'build')]
+    for dir in clean_dir:
+        try:
+            for fn in os.listdir(dir):
+                fn = os.path.join(dir, fn)
+                if os.path.isdir(fn):
+                    clean_dir.append(fn)
+                else:
+                    os.unlink(fn)
+        except OSError:
+            pass
+
+def _get_so_suffixes():
+    suffixes = _extension_suffixes()
+    if not suffixes:
+        # bah, no C_EXTENSION available.  Occurs on pypy without cpyext
+        if sys.platform == 'win32':
+            suffixes = [".pyd"]
+        else:
+            suffixes = [".so"]
+
+    return suffixes
+
+def _ensure_dir(filename):
+    dirname = os.path.dirname(filename)
+    if dirname and not os.path.isdir(dirname):
+        os.makedirs(dirname)
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/INSTALLER b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/METADATA b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..68b17eb3007fcab16e09b0f659810c01bd9ea891
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/METADATA
@@ -0,0 +1,138 @@
+Metadata-Version: 2.3
+Name: cryptography
+Version: 43.0.3
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Security :: Cryptography
+Requires-Dist: cffi >=1.12 ; platform_python_implementation != 'PyPy'
+Requires-Dist: bcrypt >=3.1.5 ; extra == 'ssh'
+Requires-Dist: nox ; extra == 'nox'
+Requires-Dist: cryptography-vectors ==43.0.3 ; extra == 'test'
+Requires-Dist: pytest >=6.2.0 ; extra == 'test'
+Requires-Dist: pytest-benchmark ; extra == 'test'
+Requires-Dist: pytest-cov ; extra == 'test'
+Requires-Dist: pytest-xdist ; extra == 'test'
+Requires-Dist: pretend ; extra == 'test'
+Requires-Dist: certifi ; extra == 'test'
+Requires-Dist: pytest-randomly ; extra == 'test-randomorder'
+Requires-Dist: sphinx >=5.3.0 ; extra == 'docs'
+Requires-Dist: sphinx-rtd-theme >=1.1.1 ; extra == 'docs'
+Requires-Dist: pyenchant >=1.6.11 ; extra == 'docstest'
+Requires-Dist: readme-renderer ; extra == 'docstest'
+Requires-Dist: sphinxcontrib-spelling >=4.0.1 ; extra == 'docstest'
+Requires-Dist: build ; extra == 'sdist'
+Requires-Dist: ruff ; extra == 'pep8test'
+Requires-Dist: mypy ; extra == 'pep8test'
+Requires-Dist: check-sdist ; extra == 'pep8test'
+Requires-Dist: click ; extra == 'pep8test'
+Provides-Extra: ssh
+Provides-Extra: nox
+Provides-Extra: test
+Provides-Extra: test-randomorder
+Provides-Extra: docs
+Provides-Extra: docstest
+Provides-Extra: sdist
+Provides-Extra: pep8test
+License-File: LICENSE
+License-File: LICENSE.APACHE
+License-File: LICENSE.BSD
+Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers.
+Author: The cryptography developers <cryptography-dev@python.org>
+Author-email: The Python Cryptographic Authority and individual contributors <cryptography-dev@python.org>
+License: Apache-2.0 OR BSD-3-Clause
+Requires-Python: >=3.7
+Description-Content-Type: text/x-rst; charset=UTF-8
+Project-URL: homepage, https://github.com/pyca/cryptography
+Project-URL: documentation, https://cryptography.io/
+Project-URL: source, https://github.com/pyca/cryptography/
+Project-URL: issues, https://github.com/pyca/cryptography/issues
+Project-URL: changelog, https://cryptography.io/en/latest/changelog/
+
+pyca/cryptography
+=================
+
+.. image:: https://img.shields.io/pypi/v/cryptography.svg
+    :target: https://pypi.org/project/cryptography/
+    :alt: Latest Version
+
+.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest
+    :target: https://cryptography.io
+    :alt: Latest Docs
+
+.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main
+    :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain
+
+
+``cryptography`` is a package which provides cryptographic recipes and
+primitives to Python developers. Our goal is for it to be your "cryptographic
+standard library". It supports Python 3.7+ and PyPy3 7.3.11+.
+
+``cryptography`` includes both high level recipes and low level interfaces to
+common cryptographic algorithms such as symmetric ciphers, message digests, and
+key derivation functions. For example, to encrypt something with
+``cryptography``'s high level symmetric encryption recipe:
+
+.. code-block:: pycon
+
+    >>> from cryptography.fernet import Fernet
+    >>> # Put this somewhere safe!
+    >>> key = Fernet.generate_key()
+    >>> f = Fernet(key)
+    >>> token = f.encrypt(b"A really secret message. Not for prying eyes.")
+    >>> token
+    b'...'
+    >>> f.decrypt(token)
+    b'A really secret message. Not for prying eyes.'
+
+You can find more information in the `documentation`_.
+
+You can install ``cryptography`` with:
+
+.. code-block:: console
+
+    $ pip install cryptography
+
+For full details see `the installation documentation`_.
+
+Discussion
+~~~~~~~~~~
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+We maintain a `cryptography-dev`_ mailing list for development discussion.
+
+You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get
+involved.
+
+Security
+~~~~~~~~
+
+Need to report a security issue? Please consult our `security reporting`_
+documentation.
+
+
+.. _`documentation`: https://cryptography.io/
+.. _`the installation documentation`: https://cryptography.io/en/latest/installation/
+.. _`issue tracker`: https://github.com/pyca/cryptography/issues
+.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev
+.. _`security reporting`: https://cryptography.io/en/latest/security/
+
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/RECORD b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..2733b8dd4e17137e71802a958238572e3d025204
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/RECORD
@@ -0,0 +1,173 @@
+cryptography-43.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+cryptography-43.0.3.dist-info/METADATA,sha256=6zbg5CUehHnvNpZEQHVe8ivt1BG6h6k_cm-o5bsOZLA,5440
+cryptography-43.0.3.dist-info/RECORD,,
+cryptography-43.0.3.dist-info/WHEEL,sha256=eVu0XH81yBgZqhoEeJXrwFbrjeVlWHiwBvasYyEEOt4,143
+cryptography-43.0.3.dist-info/license_files/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197
+cryptography-43.0.3.dist-info/license_files/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360
+cryptography-43.0.3.dist-info/license_files/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532
+cryptography/__about__.py,sha256=-FkHKD9mSuEfH37wsSKnQzJZmL5zUAUTpB5OeUQjPE0,445
+cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364
+cryptography/__pycache__/__about__.cpython-38.pyc,,
+cryptography/__pycache__/__init__.cpython-38.pyc,,
+cryptography/__pycache__/exceptions.cpython-38.pyc,,
+cryptography/__pycache__/fernet.cpython-38.pyc,,
+cryptography/__pycache__/utils.cpython-38.pyc,,
+cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087
+cryptography/fernet.py,sha256=aPj82w-Z_1GBXUtWRUsZdVbMwRo5Mbjj0wkA9wG4rkw,6696
+cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455
+cryptography/hazmat/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/__pycache__/_oid.cpython-38.pyc,,
+cryptography/hazmat/_oid.py,sha256=e9yLmxtdQtuL94ztQv3SGtt_ea1Mx6aUwGftJsP6EXk,15201
+cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361
+cryptography/hazmat/backends/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305
+cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-38.pyc,,
+cryptography/hazmat/backends/openssl/backend.py,sha256=pUXUbugLwMm2Gls-h5U5fw2RvepaNjEvnao6CTmL1xQ,9648
+cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
+cryptography/hazmat/bindings/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/bindings/_rust.abi3.so,sha256=n1mVekm7FMkR-n-qmpGRQCDDyIt8vBE6_pxQEaawIEo,10852776
+cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=wb1OT76lG19vjq97_q2MM3qdJlQhyloXfVbKFDmRse4,737
+cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=mpNJLuYLbCVrd5i33FBTmWwL_55Dw7JPkSLlSX9Q7oI,230
+cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354
+cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640
+cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=R-xJ-XmJZ1lOk-fWHHvRnP3QNTCFnKv-l3xlNWfLVt4,868
+cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=Lvn250QMdPyeF-hoBF6rkQgHLBJxVauXCb8i8uYTomQ,1368
+cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=i0gA3jUQ4rkJXTGGZrq-AuY-VQLN31lyDeWuDZ0zJYw,2553
+cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=iK0ZhQ-WyCQbjaraaFgK6q4PpD-7Rf5RDHkFD3YEW_g,1301
+cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564
+cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564
+cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299
+cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691
+cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=OJsrblS2nHptZctva-pAKFL5q8yPEAkhmjPZpJ6TA94,493
+cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=SkPHK2HdbYN02TVQEUOgW3iTdiEY7HBE4DijpdkAzmk,475
+cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=J8HoN0GdtPcjRAfNHr5Elva_nkmQfq63L75_z9dd8Uc,573
+cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=ZmLJ73pmxcZFC1XosWEiXMRYtvJJor3ZLdCQOJu85Cw,662
+cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=wPS5c7NLspM2632II0I4iH1RSxZvSRtBOVqmpyQATfk,544
+cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=JSrlGNaW49ZCZ1hcb-YJdS1EAbsMwRbVEcLL0P9OApA,872
+cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=9iogF7Q4i81IkOS-IMXp6HvxFF_3cNy_ucrAjVQnn14,540
+cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364
+cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=2BKdbrddM_9SMUpdvHKGhb9MNjURCarPxccbUDzHeoA,484
+cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=AoRMWNvCJTiH5L-lkIkCdPlrPLUdJvvfXpIvf1GmxpM,466
+cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=afhB_6M8xI1MIE5vxkaDF1jSxA48ib1--NiOxtf6boM,1394
+cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=QCmuA0IgDr4iOecUOXgUUeh3BAjJx8ubjz__EnNbyGY,972
+cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=Xo1Gd7bh9rU4HuIS4pm9UwCY6IS1gInvFwmhABLOVO4,936
+cryptography/hazmat/bindings/_rust/x509.pyi,sha256=WLrGmqmFss8dXKhlG_J9nVhoCcodR72xJdCoxEuBtjY,3551
+cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
+cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-38.pyc,,
+cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-38.pyc,,
+cryptography/hazmat/bindings/openssl/_conditional.py,sha256=dkGKGU-22uR2ZKeOOwaSxEJCGaafgUjb2romWcu03QE,5163
+cryptography/hazmat/bindings/openssl/binding.py,sha256=e1gnFAZBPrkJ3CsiZV-ug6kaPdNTAEROaUFiFrUh71M,4042
+cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
+cryptography/hazmat/decrepit/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
+cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-38.pyc,,
+cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=HWA4PKDS2w4D2dQoRerpLRU7Kntt5vJeJC7j--AlZVU,2520
+cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
+cryptography/hazmat/primitives/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/_serialization.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/cmac.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/constant_time.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/hashes.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/hmac.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/keywrap.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/padding.cpython-38.pyc,,
+cryptography/hazmat/primitives/__pycache__/poly1305.cpython-38.pyc,,
+cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532
+cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=gKa0WrLz6K4fqhnGbfBYKDSxgLxsPU0uj_EK2UT47W4,1495
+cryptography/hazmat/primitives/_serialization.py,sha256=qrozc8fw2WZSbjk3DAlSl3ResxpauwJ74ZgGoUL-mj0,5142
+cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
+cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-38.pyc,,
+cryptography/hazmat/primitives/asymmetric/dh.py,sha256=OOCjMClH1Bf14Sy7jAdwzEeCxFPb8XUe2qePbExvXwc,3420
+cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=xBwdf0pZOgvqjUKcO7Q0L3NxwalYj0SJDUqThemhSmI,3945
+cryptography/hazmat/primitives/asymmetric/ec.py,sha256=lwZmtAwi3PM8lsY1MsNaby_bVi--49OCxwE_1yqKC-A,10428
+cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=kl63fg7myuMjNTmMoVFeH6iVr0x5FkjNmggxIRTloJk,3423
+cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=2UzEDzzfkPn83UFVFlMZfIMbAixxY09WmQyrwinWTn8,3456
+cryptography/hazmat/primitives/asymmetric/padding.py,sha256=eZcvUqVLbe3u48SunLdeniaPlV4-k6pwBl67OW4jSy8,2885
+cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=nW_Ko7PID9UBJF10GVJOc_1L00ymFsfZDUJYtM5kfGQ,7637
+cryptography/hazmat/primitives/asymmetric/types.py,sha256=LnsOJym-wmPUJ7Knu_7bCNU3kIiELCd6krOaW_JU08I,2996
+cryptography/hazmat/primitives/asymmetric/utils.py,sha256=DPTs6T4F-UhwzFQTh-1fSEpQzazH2jf2xpIro3ItF4o,790
+cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=VGYuRdIYuVBtizpFdNWd2bTrT10JRa1admQdBr08xz8,3341
+cryptography/hazmat/primitives/asymmetric/x448.py,sha256=GKKJBqYLr03VewMF18bXIM941aaWcZIQ4rC02GLLEmw,3374
+cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680
+cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-38.pyc,,
+cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-38.pyc,,
+cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-38.pyc,,
+cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-38.pyc,,
+cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634
+cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=QvBMDmphRZfNmykij58L5eDkd_2NnCzIpJpyX2QwMxc,4223
+cryptography/hazmat/primitives/ciphers/base.py,sha256=tg-XNaKUyETBi7ounGDEL1_ICn-s4FF9LR7moV58blI,4211
+cryptography/hazmat/primitives/ciphers/modes.py,sha256=BFpxEGSaxoeZjrQ4sqpyPDvKClrqfDKIBv7kYtFURhE,8192
+cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338
+cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422
+cryptography/hazmat/primitives/hashes.py,sha256=EvDIJBhj83Z7f-oHbsA0TzZLFSDV_Yv8hQRdM4o8FD0,5091
+cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423
+cryptography/hazmat/primitives/kdf/__init__.py,sha256=4XibZnrYq4hh5xBjWiIXzaYW6FKx8hPbVaa_cB9zS64,750
+cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-38.pyc,,
+cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=bcn4NGXse-EsFl7nlU83e5ilop7TSHcX-CJJS107W80,3686
+cryptography/hazmat/primitives/kdf/hkdf.py,sha256=uhN5L87w4JvtAqQcPh_Ji2TPSc18IDThpaYJiHOWy3A,3015
+cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=eSuLK1sATkamgCAit794jLr7sDNlu5X0USdcWhwJdmk,9146
+cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xj3YIeX30h2BUaoJAtOo1RMXV_em0-eCG0PU_0FHJzM,1950
+cryptography/hazmat/primitives/kdf/scrypt.py,sha256=4QONhjxA_ZtuQtQ7QV3FnbB8ftrFnM52B4HPfV7hFys,2354
+cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=wCpWmwQjZ2vAu2rlk3R_PX0nINl8WGXYBmlyMOC5iPw,1992
+cryptography/hazmat/primitives/keywrap.py,sha256=XV4Pj2fqSeD-RqZVvY2cA3j5_7RwJSFygYuLfk2ujCo,5650
+cryptography/hazmat/primitives/padding.py,sha256=QUq0n-EAgEan9aQzuTsiJYGKbWiK1nSHkcYjDF1L1ok,5518
+cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355
+cryptography/hazmat/primitives/serialization/__init__.py,sha256=jyNx_7NcOEbVRBY4nP9ks0IVXBafbcYnTK27vafPLW8,1653
+cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-38.pyc,,
+cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-38.pyc,,
+cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-38.pyc,,
+cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-38.pyc,,
+cryptography/hazmat/primitives/serialization/base.py,sha256=ikq5MJIwp_oUnjiaBco_PmQwOTYuGi-XkYUYHKy8Vo0,615
+cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=7vVXbiP7qhhvKAHJT_M8-LBZdbpOwrpWRHWxNrNqzXE,4492
+cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=CNzcsuDMyEFMe3EUii4NfJlQzmakB2hLlfRFYObnHRs,11141
+cryptography/hazmat/primitives/serialization/ssh.py,sha256=VKscMrVdYK5B9PQISjjdRMglRvqa_L3sDNm5vdjVHJY,51915
+cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258
+cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-38.pyc,,
+cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-38.pyc,,
+cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-38.pyc,,
+cryptography/hazmat/primitives/twofactor/hotp.py,sha256=l1YdRMIhfPIuHKkA66keBDHhNbnBAlh6-O44P-OHIK8,2976
+cryptography/hazmat/primitives/twofactor/totp.py,sha256=v0y0xKwtYrP83ypOo5Ofd441RJLOkaFfjmp554jo5F0,1450
+cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+cryptography/utils.py,sha256=Rp7ppg4XIBVVzNQ6XngGndwkICJoYp6FoFOOgTWLJ7g,3925
+cryptography/x509/__init__.py,sha256=uGdiViR7KFnWGoJFVUStt-e_ufomWc87RQBGAZ7dT-4,7980
+cryptography/x509/__pycache__/__init__.cpython-38.pyc,,
+cryptography/x509/__pycache__/base.cpython-38.pyc,,
+cryptography/x509/__pycache__/certificate_transparency.cpython-38.pyc,,
+cryptography/x509/__pycache__/extensions.cpython-38.pyc,,
+cryptography/x509/__pycache__/general_name.cpython-38.pyc,,
+cryptography/x509/__pycache__/name.cpython-38.pyc,,
+cryptography/x509/__pycache__/ocsp.cpython-38.pyc,,
+cryptography/x509/__pycache__/oid.cpython-38.pyc,,
+cryptography/x509/__pycache__/verification.cpython-38.pyc,,
+cryptography/x509/base.py,sha256=3NbbUn9wPruhmoPO7Cl3trc3SrqV2OFIBBE0P2l05mg,37081
+cryptography/x509/certificate_transparency.py,sha256=6HvzAD0dlSQVxy6tnDhGj0-pisp1MaJ9bxQNRr92inI,2261
+cryptography/x509/extensions.py,sha256=R70KkJ_c5NQ6Kx7Rho0sGJ0Rh-bOuBHjVOFSQGRAFCs,67370
+cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836
+cryptography/x509/name.py,sha256=MYCxCSTQTpzhjxFPZaANqJ9fGrhESH73vPkoay8HSWM,14830
+cryptography/x509/ocsp.py,sha256=P6A02msz5pe-IkUFpvxezHvnEHGvPdXiD3S0wsuf4-I,20003
+cryptography/x509/oid.py,sha256=X8EbhkRTLrGuv9vHZSGqPd9zpvRVsonU_joWAL5LLY8,885
+cryptography/x509/verification.py,sha256=alfx3VaTSb2bMz7_7s788oL90vzgHwBjVINssdz0Gv0,796
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/WHEEL b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..a0fde761bbc925731a7901bf0f6fa592b40fdf80
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: maturin (1.7.0)
+Root-Is-Purelib: false
+Tag: cp37-abi3-manylinux_2_17_x86_64
+Tag: cp37-abi3-manylinux2014_x86_64
+
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..b11f379efe1504d235b4d2d42685ba5dc6af6e9f
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE
@@ -0,0 +1,3 @@
+This software is made available under the terms of *either* of the licenses
+found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made
+under the terms of *both* these licenses.
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE.APACHE b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE.APACHE
new file mode 100644
index 0000000000000000000000000000000000000000..62589edd12a37dd28b6b6fed1e2d728ac9f05c8d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE.APACHE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        https://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       https://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE.BSD b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE.BSD
new file mode 100644
index 0000000000000000000000000000000000000000..ec1a29d34d6e419411c75523408aca72f705345c
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography-43.0.3.dist-info/license_files/LICENSE.BSD
@@ -0,0 +1,27 @@
+Copyright (c) Individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+    3. Neither the name of PyCA Cryptography nor the names of its contributors
+       may be used to endorse or promote products derived from this software
+       without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/.venv/lib/python3.8/site-packages/cryptography/__about__.py b/.venv/lib/python3.8/site-packages/cryptography/__about__.py
new file mode 100644
index 0000000000000000000000000000000000000000..375ffbe84213f375a148ac5c47f7d908b0ac3cd8
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/__about__.py
@@ -0,0 +1,17 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+__all__ = [
+    "__author__",
+    "__copyright__",
+    "__version__",
+]
+
+__version__ = "43.0.3"
+
+
+__author__ = "The Python Cryptographic Authority and individual contributors"
+__copyright__ = f"Copyright 2013-2024 {__author__}"
diff --git a/.venv/lib/python3.8/site-packages/cryptography/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d374f752dfd53c6f9a12c69309fdcb6eee765251
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/__init__.py
@@ -0,0 +1,13 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.__about__ import __author__, __copyright__, __version__
+
+__all__ = [
+    "__author__",
+    "__copyright__",
+    "__version__",
+]
diff --git a/.venv/lib/python3.8/site-packages/cryptography/exceptions.py b/.venv/lib/python3.8/site-packages/cryptography/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe125ea9a763ad731ba99e4761d6623e1d2c639c
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/exceptions.py
@@ -0,0 +1,52 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions
+
+if typing.TYPE_CHECKING:
+    from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+
+_Reasons = rust_exceptions._Reasons
+
+
+class UnsupportedAlgorithm(Exception):
+    def __init__(self, message: str, reason: _Reasons | None = None) -> None:
+        super().__init__(message)
+        self._reason = reason
+
+
+class AlreadyFinalized(Exception):
+    pass
+
+
+class AlreadyUpdated(Exception):
+    pass
+
+
+class NotYetFinalized(Exception):
+    pass
+
+
+class InvalidTag(Exception):
+    pass
+
+
+class InvalidSignature(Exception):
+    pass
+
+
+class InternalError(Exception):
+    def __init__(
+        self, msg: str, err_code: list[rust_openssl.OpenSSLError]
+    ) -> None:
+        super().__init__(msg)
+        self.err_code = err_code
+
+
+class InvalidKey(Exception):
+    pass
diff --git a/.venv/lib/python3.8/site-packages/cryptography/fernet.py b/.venv/lib/python3.8/site-packages/cryptography/fernet.py
new file mode 100644
index 0000000000000000000000000000000000000000..35ce1131a921fbf9d13f114963e5a4f6e4889a9b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/fernet.py
@@ -0,0 +1,215 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import base64
+import binascii
+import os
+import time
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.primitives import hashes, padding
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.primitives.hmac import HMAC
+
+
+class InvalidToken(Exception):
+    pass
+
+
+_MAX_CLOCK_SKEW = 60
+
+
+class Fernet:
+    def __init__(
+        self,
+        key: bytes | str,
+        backend: typing.Any = None,
+    ) -> None:
+        try:
+            key = base64.urlsafe_b64decode(key)
+        except binascii.Error as exc:
+            raise ValueError(
+                "Fernet key must be 32 url-safe base64-encoded bytes."
+            ) from exc
+        if len(key) != 32:
+            raise ValueError(
+                "Fernet key must be 32 url-safe base64-encoded bytes."
+            )
+
+        self._signing_key = key[:16]
+        self._encryption_key = key[16:]
+
+    @classmethod
+    def generate_key(cls) -> bytes:
+        return base64.urlsafe_b64encode(os.urandom(32))
+
+    def encrypt(self, data: bytes) -> bytes:
+        return self.encrypt_at_time(data, int(time.time()))
+
+    def encrypt_at_time(self, data: bytes, current_time: int) -> bytes:
+        iv = os.urandom(16)
+        return self._encrypt_from_parts(data, current_time, iv)
+
+    def _encrypt_from_parts(
+        self, data: bytes, current_time: int, iv: bytes
+    ) -> bytes:
+        utils._check_bytes("data", data)
+
+        padder = padding.PKCS7(algorithms.AES.block_size).padder()
+        padded_data = padder.update(data) + padder.finalize()
+        encryptor = Cipher(
+            algorithms.AES(self._encryption_key),
+            modes.CBC(iv),
+        ).encryptor()
+        ciphertext = encryptor.update(padded_data) + encryptor.finalize()
+
+        basic_parts = (
+            b"\x80"
+            + current_time.to_bytes(length=8, byteorder="big")
+            + iv
+            + ciphertext
+        )
+
+        h = HMAC(self._signing_key, hashes.SHA256())
+        h.update(basic_parts)
+        hmac = h.finalize()
+        return base64.urlsafe_b64encode(basic_parts + hmac)
+
+    def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes:
+        timestamp, data = Fernet._get_unverified_token_data(token)
+        if ttl is None:
+            time_info = None
+        else:
+            time_info = (ttl, int(time.time()))
+        return self._decrypt_data(data, timestamp, time_info)
+
+    def decrypt_at_time(
+        self, token: bytes | str, ttl: int, current_time: int
+    ) -> bytes:
+        if ttl is None:
+            raise ValueError(
+                "decrypt_at_time() can only be used with a non-None ttl"
+            )
+        timestamp, data = Fernet._get_unverified_token_data(token)
+        return self._decrypt_data(data, timestamp, (ttl, current_time))
+
+    def extract_timestamp(self, token: bytes | str) -> int:
+        timestamp, data = Fernet._get_unverified_token_data(token)
+        # Verify the token was not tampered with.
+        self._verify_signature(data)
+        return timestamp
+
+    @staticmethod
+    def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]:
+        if not isinstance(token, (str, bytes)):
+            raise TypeError("token must be bytes or str")
+
+        try:
+            data = base64.urlsafe_b64decode(token)
+        except (TypeError, binascii.Error):
+            raise InvalidToken
+
+        if not data or data[0] != 0x80:
+            raise InvalidToken
+
+        if len(data) < 9:
+            raise InvalidToken
+
+        timestamp = int.from_bytes(data[1:9], byteorder="big")
+        return timestamp, data
+
+    def _verify_signature(self, data: bytes) -> None:
+        h = HMAC(self._signing_key, hashes.SHA256())
+        h.update(data[:-32])
+        try:
+            h.verify(data[-32:])
+        except InvalidSignature:
+            raise InvalidToken
+
+    def _decrypt_data(
+        self,
+        data: bytes,
+        timestamp: int,
+        time_info: tuple[int, int] | None,
+    ) -> bytes:
+        if time_info is not None:
+            ttl, current_time = time_info
+            if timestamp + ttl < current_time:
+                raise InvalidToken
+
+            if current_time + _MAX_CLOCK_SKEW < timestamp:
+                raise InvalidToken
+
+        self._verify_signature(data)
+
+        iv = data[9:25]
+        ciphertext = data[25:-32]
+        decryptor = Cipher(
+            algorithms.AES(self._encryption_key), modes.CBC(iv)
+        ).decryptor()
+        plaintext_padded = decryptor.update(ciphertext)
+        try:
+            plaintext_padded += decryptor.finalize()
+        except ValueError:
+            raise InvalidToken
+        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
+
+        unpadded = unpadder.update(plaintext_padded)
+        try:
+            unpadded += unpadder.finalize()
+        except ValueError:
+            raise InvalidToken
+        return unpadded
+
+
+class MultiFernet:
+    def __init__(self, fernets: typing.Iterable[Fernet]):
+        fernets = list(fernets)
+        if not fernets:
+            raise ValueError(
+                "MultiFernet requires at least one Fernet instance"
+            )
+        self._fernets = fernets
+
+    def encrypt(self, msg: bytes) -> bytes:
+        return self.encrypt_at_time(msg, int(time.time()))
+
+    def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes:
+        return self._fernets[0].encrypt_at_time(msg, current_time)
+
+    def rotate(self, msg: bytes | str) -> bytes:
+        timestamp, data = Fernet._get_unverified_token_data(msg)
+        for f in self._fernets:
+            try:
+                p = f._decrypt_data(data, timestamp, None)
+                break
+            except InvalidToken:
+                pass
+        else:
+            raise InvalidToken
+
+        iv = os.urandom(16)
+        return self._fernets[0]._encrypt_from_parts(p, timestamp, iv)
+
+    def decrypt(self, msg: bytes | str, ttl: int | None = None) -> bytes:
+        for f in self._fernets:
+            try:
+                return f.decrypt(msg, ttl)
+            except InvalidToken:
+                pass
+        raise InvalidToken
+
+    def decrypt_at_time(
+        self, msg: bytes | str, ttl: int, current_time: int
+    ) -> bytes:
+        for f in self._fernets:
+            try:
+                return f.decrypt_at_time(msg, ttl, current_time)
+            except InvalidToken:
+                pass
+        raise InvalidToken
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9f1187011bdaa0720bc462564582393700f3d4a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/__init__.py
@@ -0,0 +1,13 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+"""
+Hazardous Materials
+
+This is a "Hazardous Materials" module. You should ONLY use it if you're
+100% absolutely sure that you know what you're doing because this module
+is full of land mines, dragons, and dinosaurs with laser guns.
+"""
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/_oid.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/_oid.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd5e37d9e2ff6c75650fdcd406d4fa1fcedbfc9d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/_oid.py
@@ -0,0 +1,313 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import (
+    ObjectIdentifier as ObjectIdentifier,
+)
+from cryptography.hazmat.primitives import hashes
+
+
+class ExtensionOID:
+    SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9")
+    SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14")
+    KEY_USAGE = ObjectIdentifier("2.5.29.15")
+    SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17")
+    ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18")
+    BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19")
+    NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30")
+    CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31")
+    CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32")
+    POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33")
+    AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35")
+    POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36")
+    EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37")
+    FRESHEST_CRL = ObjectIdentifier("2.5.29.46")
+    INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54")
+    ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28")
+    AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1")
+    SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11")
+    OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5")
+    TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24")
+    CRL_NUMBER = ObjectIdentifier("2.5.29.20")
+    DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27")
+    PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier(
+        "1.3.6.1.4.1.11129.2.4.2"
+    )
+    PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")
+    SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5")
+    MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7")
+
+
+class OCSPExtensionOID:
+    NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2")
+    ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4")
+
+
+class CRLEntryExtensionOID:
+    CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29")
+    CRL_REASON = ObjectIdentifier("2.5.29.21")
+    INVALIDITY_DATE = ObjectIdentifier("2.5.29.24")
+
+
+class NameOID:
+    COMMON_NAME = ObjectIdentifier("2.5.4.3")
+    COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
+    LOCALITY_NAME = ObjectIdentifier("2.5.4.7")
+    STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8")
+    STREET_ADDRESS = ObjectIdentifier("2.5.4.9")
+    ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97")
+    ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10")
+    ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11")
+    SERIAL_NUMBER = ObjectIdentifier("2.5.4.5")
+    SURNAME = ObjectIdentifier("2.5.4.4")
+    GIVEN_NAME = ObjectIdentifier("2.5.4.42")
+    TITLE = ObjectIdentifier("2.5.4.12")
+    INITIALS = ObjectIdentifier("2.5.4.43")
+    GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44")
+    X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45")
+    DN_QUALIFIER = ObjectIdentifier("2.5.4.46")
+    PSEUDONYM = ObjectIdentifier("2.5.4.65")
+    USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1")
+    DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25")
+    EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1")
+    JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3")
+    JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1")
+    JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier(
+        "1.3.6.1.4.1.311.60.2.1.2"
+    )
+    BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15")
+    POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16")
+    POSTAL_CODE = ObjectIdentifier("2.5.4.17")
+    INN = ObjectIdentifier("1.2.643.3.131.1.1")
+    OGRN = ObjectIdentifier("1.2.643.100.1")
+    SNILS = ObjectIdentifier("1.2.643.100.3")
+    UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2")
+
+
+class SignatureAlgorithmOID:
+    RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4")
+    RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5")
+    # This is an alternate OID for RSA with SHA1 that is occasionally seen
+    _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29")
+    RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14")
+    RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11")
+    RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12")
+    RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13")
+    RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13")
+    RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14")
+    RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15")
+    RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16")
+    RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10")
+    ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1")
+    ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1")
+    ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2")
+    ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3")
+    ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4")
+    ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9")
+    ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10")
+    ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11")
+    ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12")
+    DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3")
+    DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1")
+    DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2")
+    DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3")
+    DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4")
+    ED25519 = ObjectIdentifier("1.3.101.112")
+    ED448 = ObjectIdentifier("1.3.101.113")
+    GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3")
+    GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2")
+    GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3")
+
+
+_SIG_OIDS_TO_HASH: dict[ObjectIdentifier, hashes.HashAlgorithm | None] = {
+    SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(),
+    SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(),
+    SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(),
+    SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(),
+    SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(),
+    SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(),
+    SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(),
+    SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(),
+    SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(),
+    SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(),
+    SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(),
+    SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(),
+    SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(),
+    SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(),
+    SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(),
+    SignatureAlgorithmOID.ED25519: None,
+    SignatureAlgorithmOID.ED448: None,
+    SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None,
+    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None,
+    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None,
+}
+
+
+class PublicKeyAlgorithmOID:
+    DSA = ObjectIdentifier("1.2.840.10040.4.1")
+    EC_PUBLIC_KEY = ObjectIdentifier("1.2.840.10045.2.1")
+    RSAES_PKCS1_v1_5 = ObjectIdentifier("1.2.840.113549.1.1.1")
+    RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10")
+    X25519 = ObjectIdentifier("1.3.101.110")
+    X448 = ObjectIdentifier("1.3.101.111")
+    ED25519 = ObjectIdentifier("1.3.101.112")
+    ED448 = ObjectIdentifier("1.3.101.113")
+
+
+class ExtendedKeyUsageOID:
+    SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1")
+    CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2")
+    CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3")
+    EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4")
+    TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
+    OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
+    ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0")
+    SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2")
+    KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5")
+    IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17")
+    CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4")
+
+
+class AuthorityInformationAccessOID:
+    CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
+    OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")
+
+
+class SubjectInformationAccessOID:
+    CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5")
+
+
+class CertificatePoliciesOID:
+    CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1")
+    CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2")
+    ANY_POLICY = ObjectIdentifier("2.5.29.32.0")
+
+
+class AttributeOID:
+    CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7")
+    UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2")
+
+
+_OID_NAMES = {
+    NameOID.COMMON_NAME: "commonName",
+    NameOID.COUNTRY_NAME: "countryName",
+    NameOID.LOCALITY_NAME: "localityName",
+    NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName",
+    NameOID.STREET_ADDRESS: "streetAddress",
+    NameOID.ORGANIZATION_NAME: "organizationName",
+    NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName",
+    NameOID.SERIAL_NUMBER: "serialNumber",
+    NameOID.SURNAME: "surname",
+    NameOID.GIVEN_NAME: "givenName",
+    NameOID.TITLE: "title",
+    NameOID.GENERATION_QUALIFIER: "generationQualifier",
+    NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier",
+    NameOID.DN_QUALIFIER: "dnQualifier",
+    NameOID.PSEUDONYM: "pseudonym",
+    NameOID.USER_ID: "userID",
+    NameOID.DOMAIN_COMPONENT: "domainComponent",
+    NameOID.EMAIL_ADDRESS: "emailAddress",
+    NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName",
+    NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName",
+    NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: (
+        "jurisdictionStateOrProvinceName"
+    ),
+    NameOID.BUSINESS_CATEGORY: "businessCategory",
+    NameOID.POSTAL_ADDRESS: "postalAddress",
+    NameOID.POSTAL_CODE: "postalCode",
+    NameOID.INN: "INN",
+    NameOID.OGRN: "OGRN",
+    NameOID.SNILS: "SNILS",
+    NameOID.UNSTRUCTURED_NAME: "unstructuredName",
+    SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption",
+    SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption",
+    SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption",
+    SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption",
+    SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption",
+    SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption",
+    SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS",
+    SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1",
+    SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224",
+    SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256",
+    SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384",
+    SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512",
+    SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1",
+    SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224",
+    SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256",
+    SignatureAlgorithmOID.ED25519: "ed25519",
+    SignatureAlgorithmOID.ED448: "ed448",
+    SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: (
+        "GOST R 34.11-94 with GOST R 34.10-2001"
+    ),
+    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: (
+        "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)"
+    ),
+    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: (
+        "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)"
+    ),
+    PublicKeyAlgorithmOID.DSA: "dsaEncryption",
+    PublicKeyAlgorithmOID.EC_PUBLIC_KEY: "id-ecPublicKey",
+    PublicKeyAlgorithmOID.RSAES_PKCS1_v1_5: "rsaEncryption",
+    PublicKeyAlgorithmOID.RSASSA_PSS: "rsassaPss",
+    PublicKeyAlgorithmOID.X25519: "X25519",
+    PublicKeyAlgorithmOID.X448: "X448",
+    ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth",
+    ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth",
+    ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning",
+    ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection",
+    ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping",
+    ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning",
+    ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin",
+    ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC",
+    ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes",
+    ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier",
+    ExtensionOID.KEY_USAGE: "keyUsage",
+    ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName",
+    ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName",
+    ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints",
+    ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: (
+        "signedCertificateTimestampList"
+    ),
+    ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: (
+        "signedCertificateTimestampList"
+    ),
+    ExtensionOID.PRECERT_POISON: "ctPoison",
+    ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate",
+    CRLEntryExtensionOID.CRL_REASON: "cRLReason",
+    CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate",
+    CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer",
+    ExtensionOID.NAME_CONSTRAINTS: "nameConstraints",
+    ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints",
+    ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies",
+    ExtensionOID.POLICY_MAPPINGS: "policyMappings",
+    ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier",
+    ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints",
+    ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage",
+    ExtensionOID.FRESHEST_CRL: "freshestCRL",
+    ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy",
+    ExtensionOID.ISSUING_DISTRIBUTION_POINT: "issuingDistributionPoint",
+    ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess",
+    ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess",
+    ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck",
+    ExtensionOID.CRL_NUMBER: "cRLNumber",
+    ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator",
+    ExtensionOID.TLS_FEATURE: "TLSFeature",
+    AuthorityInformationAccessOID.OCSP: "OCSP",
+    AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers",
+    SubjectInformationAccessOID.CA_REPOSITORY: "caRepository",
+    CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps",
+    CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice",
+    OCSPExtensionOID.NONCE: "OCSPNonce",
+    AttributeOID.CHALLENGE_PASSWORD: "challengePassword",
+}
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4400aa037451d10d6bcadd455dcf98f9b72e221
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/__init__.py
@@ -0,0 +1,13 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from typing import Any
+
+
+def default_backend() -> Any:
+    from cryptography.hazmat.backends.openssl.backend import backend
+
+    return backend
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..51b04476cbb7f4a98051f2fc55bcc1aa35e22d05
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/__init__.py
@@ -0,0 +1,9 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.backends.openssl.backend import backend
+
+__all__ = ["backend"]
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/backend.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/backend.py
new file mode 100644
index 0000000000000000000000000000000000000000..c87d3e8482366bd7faa8b9632ae973745e42fa0f
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/backend.py
@@ -0,0 +1,291 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.bindings.openssl import binding
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
+from cryptography.hazmat.primitives.asymmetric.padding import (
+    MGF1,
+    OAEP,
+    PSS,
+    PKCS1v15,
+)
+from cryptography.hazmat.primitives.ciphers import (
+    CipherAlgorithm,
+)
+from cryptography.hazmat.primitives.ciphers.algorithms import (
+    AES,
+)
+from cryptography.hazmat.primitives.ciphers.modes import (
+    CBC,
+    Mode,
+)
+
+
+class Backend:
+    """
+    OpenSSL API binding interfaces.
+    """
+
+    name = "openssl"
+
+    # TripleDES encryption is disallowed/deprecated throughout 2023 in
+    # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA).
+    _fips_ciphers = (AES,)
+    # Sometimes SHA1 is still permissible. That logic is contained
+    # within the various *_supported methods.
+    _fips_hashes = (
+        hashes.SHA224,
+        hashes.SHA256,
+        hashes.SHA384,
+        hashes.SHA512,
+        hashes.SHA512_224,
+        hashes.SHA512_256,
+        hashes.SHA3_224,
+        hashes.SHA3_256,
+        hashes.SHA3_384,
+        hashes.SHA3_512,
+        hashes.SHAKE128,
+        hashes.SHAKE256,
+    )
+    _fips_ecdh_curves = (
+        ec.SECP224R1,
+        ec.SECP256R1,
+        ec.SECP384R1,
+        ec.SECP521R1,
+    )
+    _fips_rsa_min_key_size = 2048
+    _fips_rsa_min_public_exponent = 65537
+    _fips_dsa_min_modulus = 1 << 2048
+    _fips_dh_min_key_size = 2048
+    _fips_dh_min_modulus = 1 << _fips_dh_min_key_size
+
+    def __init__(self) -> None:
+        self._binding = binding.Binding()
+        self._ffi = self._binding.ffi
+        self._lib = self._binding.lib
+        self._fips_enabled = rust_openssl.is_fips_enabled()
+
+    def __repr__(self) -> str:
+        return (
+            f"<OpenSSLBackend(version: {self.openssl_version_text()}, "
+            f"FIPS: {self._fips_enabled}, "
+            f"Legacy: {rust_openssl._legacy_provider_loaded})>"
+        )
+
+    def openssl_assert(self, ok: bool) -> None:
+        return binding._openssl_assert(ok)
+
+    def _enable_fips(self) -> None:
+        # This function enables FIPS mode for OpenSSL 3.0.0 on installs that
+        # have the FIPS provider installed properly.
+        rust_openssl.enable_fips(rust_openssl._providers)
+        assert rust_openssl.is_fips_enabled()
+        self._fips_enabled = rust_openssl.is_fips_enabled()
+
+    def openssl_version_text(self) -> str:
+        """
+        Friendly string name of the loaded OpenSSL library. This is not
+        necessarily the same version as it was compiled against.
+
+        Example: OpenSSL 3.2.1 30 Jan 2024
+        """
+        return rust_openssl.openssl_version_text()
+
+    def openssl_version_number(self) -> int:
+        return rust_openssl.openssl_version()
+
+    def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm):
+        if algorithm.name in ("blake2b", "blake2s"):
+            alg = f"{algorithm.name}{algorithm.digest_size * 8}".encode(
+                "ascii"
+            )
+        else:
+            alg = algorithm.name.encode("ascii")
+
+        evp_md = self._lib.EVP_get_digestbyname(alg)
+        return evp_md
+
+    def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
+        if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
+            return False
+
+        evp_md = self._evp_md_from_algorithm(algorithm)
+        return evp_md != self._ffi.NULL
+
+    def signature_hash_supported(
+        self, algorithm: hashes.HashAlgorithm
+    ) -> bool:
+        # Dedicated check for hashing algorithm use in message digest for
+        # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption).
+        if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
+            return False
+        return self.hash_supported(algorithm)
+
+    def scrypt_supported(self) -> bool:
+        if self._fips_enabled:
+            return False
+        else:
+            return hasattr(rust_openssl.kdf, "derive_scrypt")
+
+    def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
+        # FIPS mode still allows SHA1 for HMAC
+        if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
+            return True
+
+        return self.hash_supported(algorithm)
+
+    def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
+        if self._fips_enabled:
+            # FIPS mode requires AES. TripleDES is disallowed/deprecated in
+            # FIPS 140-3.
+            if not isinstance(cipher, self._fips_ciphers):
+                return False
+
+        return rust_openssl.ciphers.cipher_supported(cipher, mode)
+
+    def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
+        return self.hmac_supported(algorithm)
+
+    def _consume_errors(self) -> list[rust_openssl.OpenSSLError]:
+        return rust_openssl.capture_error_stack()
+
+    def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
+        if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
+            return False
+
+        return isinstance(
+            algorithm,
+            (
+                hashes.SHA1,
+                hashes.SHA224,
+                hashes.SHA256,
+                hashes.SHA384,
+                hashes.SHA512,
+            ),
+        )
+
+    def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool:
+        if isinstance(padding, PKCS1v15):
+            return True
+        elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
+            # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked
+            # as signature algorithm.
+            if self._fips_enabled and isinstance(
+                padding._mgf._algorithm, hashes.SHA1
+            ):
+                return True
+            else:
+                return self.hash_supported(padding._mgf._algorithm)
+        elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
+            return self._oaep_hash_supported(
+                padding._mgf._algorithm
+            ) and self._oaep_hash_supported(padding._algorithm)
+        else:
+            return False
+
+    def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool:
+        if self._fips_enabled and isinstance(padding, PKCS1v15):
+            return False
+        else:
+            return self.rsa_padding_supported(padding)
+
+    def dsa_supported(self) -> bool:
+        return (
+            not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
+            and not self._fips_enabled
+        )
+
+    def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
+        if not self.dsa_supported():
+            return False
+        return self.signature_hash_supported(algorithm)
+
+    def cmac_algorithm_supported(self, algorithm) -> bool:
+        return self.cipher_supported(
+            algorithm, CBC(b"\x00" * algorithm.block_size)
+        )
+
+    def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool:
+        if self._fips_enabled and not isinstance(
+            curve, self._fips_ecdh_curves
+        ):
+            return False
+
+        return rust_openssl.ec.curve_supported(curve)
+
+    def elliptic_curve_signature_algorithm_supported(
+        self,
+        signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
+        curve: ec.EllipticCurve,
+    ) -> bool:
+        # We only support ECDSA right now.
+        if not isinstance(signature_algorithm, ec.ECDSA):
+            return False
+
+        return self.elliptic_curve_supported(curve) and (
+            isinstance(signature_algorithm.algorithm, asym_utils.Prehashed)
+            or self.hash_supported(signature_algorithm.algorithm)
+        )
+
+    def elliptic_curve_exchange_algorithm_supported(
+        self, algorithm: ec.ECDH, curve: ec.EllipticCurve
+    ) -> bool:
+        return self.elliptic_curve_supported(curve) and isinstance(
+            algorithm, ec.ECDH
+        )
+
+    def dh_supported(self) -> bool:
+        return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
+
+    def dh_x942_serialization_supported(self) -> bool:
+        return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1
+
+    def x25519_supported(self) -> bool:
+        if self._fips_enabled:
+            return False
+        return True
+
+    def x448_supported(self) -> bool:
+        if self._fips_enabled:
+            return False
+        return (
+            not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
+            and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
+        )
+
+    def ed25519_supported(self) -> bool:
+        if self._fips_enabled:
+            return False
+        return True
+
+    def ed448_supported(self) -> bool:
+        if self._fips_enabled:
+            return False
+        return (
+            not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
+            and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
+        )
+
+    def ecdsa_deterministic_supported(self) -> bool:
+        return (
+            rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER
+            and not self._fips_enabled
+        )
+
+    def poly1305_supported(self) -> bool:
+        if self._fips_enabled:
+            return False
+        return True
+
+    def pkcs7_supported(self) -> bool:
+        return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
+
+
+backend = Backend()
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b509336233c2fafe4185a49da5909c8bbb38dfd7
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/__init__.py
@@ -0,0 +1,3 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust.abi3.so b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust.abi3.so
new file mode 100755
index 0000000000000000000000000000000000000000..9f6455976fc3439f27129ba6c5a49194cdf7073d
Binary files /dev/null and b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust.abi3.so differ
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..c0ea0a5405cafb459bd4ae38ddf651d8bec3620d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi
@@ -0,0 +1,24 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives import padding
+
+def check_pkcs7_padding(data: bytes) -> bool: ...
+def check_ansix923_padding(data: bytes) -> bool: ...
+
+class PKCS7PaddingContext(padding.PaddingContext):
+    def __init__(self, block_size: int) -> None: ...
+    def update(self, data: bytes) -> bytes: ...
+    def finalize(self) -> bytes: ...
+
+class ObjectIdentifier:
+    def __init__(self, val: str) -> None: ...
+    @property
+    def dotted_string(self) -> str: ...
+    @property
+    def _name(self) -> str: ...
+
+T = typing.TypeVar("T")
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..80100082acd30b6bc69e5b8ab0972d77a33dc11a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi
@@ -0,0 +1,8 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+lib = typing.Any
+ffi = typing.Any
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..3b5f208ecf09aa661854e6983507b0e7b1ef15cb
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi
@@ -0,0 +1,7 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+def decode_dss_signature(signature: bytes) -> tuple[int, int]: ...
+def encode_dss_signature(r: int, s: int) -> bytes: ...
+def parse_spki_for_data(data: bytes) -> bytes: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..09f46b1e817f806873c4a4bdd7b8466046b5f64d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi
@@ -0,0 +1,17 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+class _Reasons:
+    BACKEND_MISSING_INTERFACE: _Reasons
+    UNSUPPORTED_HASH: _Reasons
+    UNSUPPORTED_CIPHER: _Reasons
+    UNSUPPORTED_PADDING: _Reasons
+    UNSUPPORTED_MGF: _Reasons
+    UNSUPPORTED_PUBLIC_KEY_ALGORITHM: _Reasons
+    UNSUPPORTED_ELLIPTIC_CURVE: _Reasons
+    UNSUPPORTED_SERIALIZATION: _Reasons
+    UNSUPPORTED_X509: _Reasons
+    UNSUPPORTED_EXCHANGE_ALGORITHM: _Reasons
+    UNSUPPORTED_DIFFIE_HELLMAN: _Reasons
+    UNSUPPORTED_MAC: _Reasons
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..5e02145d86a52328c1bd7594150c73ced0c1e0df
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi
@@ -0,0 +1,23 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
+from cryptography.x509 import ocsp
+
+class OCSPRequest: ...
+class OCSPResponse: ...
+class OCSPSingleResponse: ...
+
+def load_der_ocsp_request(data: bytes) -> ocsp.OCSPRequest: ...
+def load_der_ocsp_response(data: bytes) -> ocsp.OCSPResponse: ...
+def create_ocsp_request(
+    builder: ocsp.OCSPRequestBuilder,
+) -> ocsp.OCSPRequest: ...
+def create_ocsp_response(
+    status: ocsp.OCSPResponseStatus,
+    builder: ocsp.OCSPResponseBuilder | None,
+    private_key: PrivateKeyTypes | None,
+    hash_algorithm: hashes.HashAlgorithm | None,
+) -> ocsp.OCSPResponse: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..1e66d333103035a75fdd118b8dc1ad1e1f817762
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi
@@ -0,0 +1,71 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.bindings._rust.openssl import (
+    aead,
+    ciphers,
+    cmac,
+    dh,
+    dsa,
+    ec,
+    ed448,
+    ed25519,
+    hashes,
+    hmac,
+    kdf,
+    keys,
+    poly1305,
+    rsa,
+    x448,
+    x25519,
+)
+
+__all__ = [
+    "aead",
+    "ciphers",
+    "cmac",
+    "dh",
+    "dsa",
+    "ec",
+    "ed448",
+    "ed25519",
+    "hashes",
+    "hmac",
+    "kdf",
+    "keys",
+    "openssl_version",
+    "openssl_version_text",
+    "poly1305",
+    "raise_openssl_error",
+    "rsa",
+    "x448",
+    "x25519",
+]
+
+CRYPTOGRAPHY_IS_LIBRESSL: bool
+CRYPTOGRAPHY_IS_BORINGSSL: bool
+CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: bool
+CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool
+
+class Providers: ...
+
+_legacy_provider_loaded: bool
+_providers: Providers
+
+def openssl_version() -> int: ...
+def openssl_version_text() -> str: ...
+def raise_openssl_error() -> typing.NoReturn: ...
+def capture_error_stack() -> list[OpenSSLError]: ...
+def is_fips_enabled() -> bool: ...
+def enable_fips(providers: Providers) -> None: ...
+
+class OpenSSLError:
+    @property
+    def lib(self) -> int: ...
+    @property
+    def reason(self) -> int: ...
+    @property
+    def reason_text(self) -> bytes: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..047f49d819c17565b454ab66aac650c15ae2cd32
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi
@@ -0,0 +1,103 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+class AESGCM:
+    def __init__(self, key: bytes) -> None: ...
+    @staticmethod
+    def generate_key(key_size: int) -> bytes: ...
+    def encrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+    def decrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+
+class ChaCha20Poly1305:
+    def __init__(self, key: bytes) -> None: ...
+    @staticmethod
+    def generate_key() -> bytes: ...
+    def encrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+    def decrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+
+class AESCCM:
+    def __init__(self, key: bytes, tag_length: int = 16) -> None: ...
+    @staticmethod
+    def generate_key(key_size: int) -> bytes: ...
+    def encrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+    def decrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+
+class AESSIV:
+    def __init__(self, key: bytes) -> None: ...
+    @staticmethod
+    def generate_key(key_size: int) -> bytes: ...
+    def encrypt(
+        self,
+        data: bytes,
+        associated_data: list[bytes] | None,
+    ) -> bytes: ...
+    def decrypt(
+        self,
+        data: bytes,
+        associated_data: list[bytes] | None,
+    ) -> bytes: ...
+
+class AESOCB3:
+    def __init__(self, key: bytes) -> None: ...
+    @staticmethod
+    def generate_key(key_size: int) -> bytes: ...
+    def encrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+    def decrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+
+class AESGCMSIV:
+    def __init__(self, key: bytes) -> None: ...
+    @staticmethod
+    def generate_key(key_size: int) -> bytes: ...
+    def encrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
+    def decrypt(
+        self,
+        nonce: bytes,
+        data: bytes,
+        associated_data: bytes | None,
+    ) -> bytes: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..759f3b591cba6b55876bf8dd12b2a87846f5dbe6
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi
@@ -0,0 +1,38 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives import ciphers
+from cryptography.hazmat.primitives.ciphers import modes
+
+@typing.overload
+def create_encryption_ctx(
+    algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag
+) -> ciphers.AEADEncryptionContext: ...
+@typing.overload
+def create_encryption_ctx(
+    algorithm: ciphers.CipherAlgorithm, mode: modes.Mode
+) -> ciphers.CipherContext: ...
+@typing.overload
+def create_decryption_ctx(
+    algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag
+) -> ciphers.AEADDecryptionContext: ...
+@typing.overload
+def create_decryption_ctx(
+    algorithm: ciphers.CipherAlgorithm, mode: modes.Mode
+) -> ciphers.CipherContext: ...
+def cipher_supported(
+    algorithm: ciphers.CipherAlgorithm, mode: modes.Mode
+) -> bool: ...
+def _advance(
+    ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int
+) -> None: ...
+def _advance_aad(
+    ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int
+) -> None: ...
+
+class CipherContext: ...
+class AEADEncryptionContext: ...
+class AEADDecryptionContext: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..9c03508bc89bf087e24c684802eeec344d76bb9e
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi
@@ -0,0 +1,18 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives import ciphers
+
+class CMAC:
+    def __init__(
+        self,
+        algorithm: ciphers.BlockCipherAlgorithm,
+        backend: typing.Any = None,
+    ) -> None: ...
+    def update(self, data: bytes) -> None: ...
+    def finalize(self) -> bytes: ...
+    def verify(self, signature: bytes) -> None: ...
+    def copy(self) -> CMAC: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..08733d745c3ddc994f358193dd4b79ce359583d8
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi
@@ -0,0 +1,51 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives.asymmetric import dh
+
+MIN_MODULUS_SIZE: int
+
+class DHPrivateKey: ...
+class DHPublicKey: ...
+class DHParameters: ...
+
+class DHPrivateNumbers:
+    def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: ...
+    def private_key(self, backend: typing.Any = None) -> dh.DHPrivateKey: ...
+    @property
+    def x(self) -> int: ...
+    @property
+    def public_numbers(self) -> DHPublicNumbers: ...
+
+class DHPublicNumbers:
+    def __init__(
+        self, y: int, parameter_numbers: DHParameterNumbers
+    ) -> None: ...
+    def public_key(self, backend: typing.Any = None) -> dh.DHPublicKey: ...
+    @property
+    def y(self) -> int: ...
+    @property
+    def parameter_numbers(self) -> DHParameterNumbers: ...
+
+class DHParameterNumbers:
+    def __init__(self, p: int, g: int, q: int | None = None) -> None: ...
+    def parameters(self, backend: typing.Any = None) -> dh.DHParameters: ...
+    @property
+    def p(self) -> int: ...
+    @property
+    def g(self) -> int: ...
+    @property
+    def q(self) -> int | None: ...
+
+def generate_parameters(
+    generator: int, key_size: int, backend: typing.Any = None
+) -> dh.DHParameters: ...
+def from_pem_parameters(
+    data: bytes, backend: typing.Any = None
+) -> dh.DHParameters: ...
+def from_der_parameters(
+    data: bytes, backend: typing.Any = None
+) -> dh.DHParameters: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..0922a4c4041a90b1e884f56522f82bced398c80e
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi
@@ -0,0 +1,41 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives.asymmetric import dsa
+
+class DSAPrivateKey: ...
+class DSAPublicKey: ...
+class DSAParameters: ...
+
+class DSAPrivateNumbers:
+    def __init__(self, x: int, public_numbers: DSAPublicNumbers) -> None: ...
+    @property
+    def x(self) -> int: ...
+    @property
+    def public_numbers(self) -> DSAPublicNumbers: ...
+    def private_key(self, backend: typing.Any = None) -> dsa.DSAPrivateKey: ...
+
+class DSAPublicNumbers:
+    def __init__(
+        self, y: int, parameter_numbers: DSAParameterNumbers
+    ) -> None: ...
+    @property
+    def y(self) -> int: ...
+    @property
+    def parameter_numbers(self) -> DSAParameterNumbers: ...
+    def public_key(self, backend: typing.Any = None) -> dsa.DSAPublicKey: ...
+
+class DSAParameterNumbers:
+    def __init__(self, p: int, q: int, g: int) -> None: ...
+    @property
+    def p(self) -> int: ...
+    @property
+    def q(self) -> int: ...
+    @property
+    def g(self) -> int: ...
+    def parameters(self, backend: typing.Any = None) -> dsa.DSAParameters: ...
+
+def generate_parameters(key_size: int) -> dsa.DSAParameters: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..5c3b7bf6e4a9bafac1423bd3936ebc347885a950
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi
@@ -0,0 +1,52 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives.asymmetric import ec
+
+class ECPrivateKey: ...
+class ECPublicKey: ...
+
+class EllipticCurvePrivateNumbers:
+    def __init__(
+        self, private_value: int, public_numbers: EllipticCurvePublicNumbers
+    ) -> None: ...
+    def private_key(
+        self, backend: typing.Any = None
+    ) -> ec.EllipticCurvePrivateKey: ...
+    @property
+    def private_value(self) -> int: ...
+    @property
+    def public_numbers(self) -> EllipticCurvePublicNumbers: ...
+
+class EllipticCurvePublicNumbers:
+    def __init__(self, x: int, y: int, curve: ec.EllipticCurve) -> None: ...
+    def public_key(
+        self, backend: typing.Any = None
+    ) -> ec.EllipticCurvePublicKey: ...
+    @property
+    def x(self) -> int: ...
+    @property
+    def y(self) -> int: ...
+    @property
+    def curve(self) -> ec.EllipticCurve: ...
+    def __eq__(self, other: object) -> bool: ...
+
+def curve_supported(curve: ec.EllipticCurve) -> bool: ...
+def generate_private_key(
+    curve: ec.EllipticCurve, backend: typing.Any = None
+) -> ec.EllipticCurvePrivateKey: ...
+def from_private_numbers(
+    numbers: ec.EllipticCurvePrivateNumbers,
+) -> ec.EllipticCurvePrivateKey: ...
+def from_public_numbers(
+    numbers: ec.EllipticCurvePublicNumbers,
+) -> ec.EllipticCurvePublicKey: ...
+def from_public_bytes(
+    curve: ec.EllipticCurve, data: bytes
+) -> ec.EllipticCurvePublicKey: ...
+def derive_private_key(
+    private_value: int, curve: ec.EllipticCurve
+) -> ec.EllipticCurvePrivateKey: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..5233f9a1d1c8e45f928bd4b41cf1e821332a2a67
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi
@@ -0,0 +1,12 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.primitives.asymmetric import ed25519
+
+class Ed25519PrivateKey: ...
+class Ed25519PublicKey: ...
+
+def generate_key() -> ed25519.Ed25519PrivateKey: ...
+def from_private_bytes(data: bytes) -> ed25519.Ed25519PrivateKey: ...
+def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..7a06520380a08886f281a5115ca07bb34004eca1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi
@@ -0,0 +1,12 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.primitives.asymmetric import ed448
+
+class Ed448PrivateKey: ...
+class Ed448PublicKey: ...
+
+def generate_key() -> ed448.Ed448PrivateKey: ...
+def from_private_bytes(data: bytes) -> ed448.Ed448PrivateKey: ...
+def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..ca5f42a006158f9cddbaddc2d5b581d556f2914d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi
@@ -0,0 +1,17 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives import hashes
+
+class Hash(hashes.HashContext):
+    def __init__(
+        self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None
+    ) -> None: ...
+    @property
+    def algorithm(self) -> hashes.HashAlgorithm: ...
+    def update(self, data: bytes) -> None: ...
+    def finalize(self) -> bytes: ...
+    def copy(self) -> Hash: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..e38d9b54d01bc6e5baea24c2ac6c3d8631941f80
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi
@@ -0,0 +1,21 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives import hashes
+
+class HMAC(hashes.HashContext):
+    def __init__(
+        self,
+        key: bytes,
+        algorithm: hashes.HashAlgorithm,
+        backend: typing.Any = None,
+    ) -> None: ...
+    @property
+    def algorithm(self) -> hashes.HashAlgorithm: ...
+    def update(self, data: bytes) -> None: ...
+    def finalize(self) -> bytes: ...
+    def verify(self, signature: bytes) -> None: ...
+    def copy(self) -> HMAC: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..034a8fed2e7898baa5acb56ce01d96ba9fcbb89e
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi
@@ -0,0 +1,22 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.primitives.hashes import HashAlgorithm
+
+def derive_pbkdf2_hmac(
+    key_material: bytes,
+    algorithm: HashAlgorithm,
+    salt: bytes,
+    iterations: int,
+    length: int,
+) -> bytes: ...
+def derive_scrypt(
+    key_material: bytes,
+    salt: bytes,
+    n: int,
+    r: int,
+    p: int,
+    max_mem: int,
+    length: int,
+) -> bytes: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..6815b7d9154ba7dbfb1ef98ccc4128387459a1a8
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi
@@ -0,0 +1,33 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives.asymmetric.types import (
+    PrivateKeyTypes,
+    PublicKeyTypes,
+)
+
+def load_der_private_key(
+    data: bytes,
+    password: bytes | None,
+    backend: typing.Any = None,
+    *,
+    unsafe_skip_rsa_key_validation: bool = False,
+) -> PrivateKeyTypes: ...
+def load_pem_private_key(
+    data: bytes,
+    password: bytes | None,
+    backend: typing.Any = None,
+    *,
+    unsafe_skip_rsa_key_validation: bool = False,
+) -> PrivateKeyTypes: ...
+def load_der_public_key(
+    data: bytes,
+    backend: typing.Any = None,
+) -> PublicKeyTypes: ...
+def load_pem_public_key(
+    data: bytes,
+    backend: typing.Any = None,
+) -> PublicKeyTypes: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..2e9b0a9e1254361860942722a03281e9bb8a3bb3
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi
@@ -0,0 +1,13 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+class Poly1305:
+    def __init__(self, key: bytes) -> None: ...
+    @staticmethod
+    def generate_tag(key: bytes, data: bytes) -> bytes: ...
+    @staticmethod
+    def verify_tag(key: bytes, data: bytes, tag: bytes) -> None: ...
+    def update(self, data: bytes) -> None: ...
+    def finalize(self) -> bytes: ...
+    def verify(self, tag: bytes) -> None: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..ef7752ddb79db62489bcf71df933fdefd83c0f9d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi
@@ -0,0 +1,55 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography.hazmat.primitives.asymmetric import rsa
+
+class RSAPrivateKey: ...
+class RSAPublicKey: ...
+
+class RSAPrivateNumbers:
+    def __init__(
+        self,
+        p: int,
+        q: int,
+        d: int,
+        dmp1: int,
+        dmq1: int,
+        iqmp: int,
+        public_numbers: RSAPublicNumbers,
+    ) -> None: ...
+    @property
+    def p(self) -> int: ...
+    @property
+    def q(self) -> int: ...
+    @property
+    def d(self) -> int: ...
+    @property
+    def dmp1(self) -> int: ...
+    @property
+    def dmq1(self) -> int: ...
+    @property
+    def iqmp(self) -> int: ...
+    @property
+    def public_numbers(self) -> RSAPublicNumbers: ...
+    def private_key(
+        self,
+        backend: typing.Any = None,
+        *,
+        unsafe_skip_rsa_key_validation: bool = False,
+    ) -> rsa.RSAPrivateKey: ...
+
+class RSAPublicNumbers:
+    def __init__(self, e: int, n: int) -> None: ...
+    @property
+    def n(self) -> int: ...
+    @property
+    def e(self) -> int: ...
+    def public_key(self, backend: typing.Any = None) -> rsa.RSAPublicKey: ...
+
+def generate_private_key(
+    public_exponent: int,
+    key_size: int,
+) -> rsa.RSAPrivateKey: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..da0f3ec588b94e6506b6cf014a6b0819b1470482
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi
@@ -0,0 +1,12 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.primitives.asymmetric import x25519
+
+class X25519PrivateKey: ...
+class X25519PublicKey: ...
+
+def generate_key() -> x25519.X25519PrivateKey: ...
+def from_private_bytes(data: bytes) -> x25519.X25519PrivateKey: ...
+def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..e51cfebe15f67688e17284341d8c6e710b321164
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi
@@ -0,0 +1,12 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.primitives.asymmetric import x448
+
+class X448PrivateKey: ...
+class X448PublicKey: ...
+
+def generate_key() -> x448.X448PrivateKey: ...
+def from_private_bytes(data: bytes) -> x448.X448PrivateKey: ...
+def from_public_bytes(data: bytes) -> x448.X448PublicKey: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..40514c4623d579a0ce9b3a961b032ad62a97a8dd
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi
@@ -0,0 +1,46 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography import x509
+from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
+from cryptography.hazmat.primitives.serialization import (
+    KeySerializationEncryption,
+)
+from cryptography.hazmat.primitives.serialization.pkcs12 import (
+    PKCS12KeyAndCertificates,
+    PKCS12PrivateKeyTypes,
+)
+
+class PKCS12Certificate:
+    def __init__(
+        self, cert: x509.Certificate, friendly_name: bytes | None
+    ) -> None: ...
+    @property
+    def friendly_name(self) -> bytes | None: ...
+    @property
+    def certificate(self) -> x509.Certificate: ...
+
+def load_key_and_certificates(
+    data: bytes,
+    password: bytes | None,
+    backend: typing.Any = None,
+) -> tuple[
+    PrivateKeyTypes | None,
+    x509.Certificate | None,
+    list[x509.Certificate],
+]: ...
+def load_pkcs12(
+    data: bytes,
+    password: bytes | None,
+    backend: typing.Any = None,
+) -> PKCS12KeyAndCertificates: ...
+def serialize_key_and_certificates(
+    name: bytes | None,
+    key: PKCS12PrivateKeyTypes | None,
+    cert: x509.Certificate | None,
+    cas: typing.Iterable[x509.Certificate | PKCS12Certificate] | None,
+    encryption_algorithm: KeySerializationEncryption,
+) -> bytes: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..a72120a762ecdf0f53919f7604d75b2375dc6ef4
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi
@@ -0,0 +1,30 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import typing
+
+from cryptography import x509
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.serialization import pkcs7
+
+def serialize_certificates(
+    certs: list[x509.Certificate],
+    encoding: serialization.Encoding,
+) -> bytes: ...
+def encrypt_and_serialize(
+    builder: pkcs7.PKCS7EnvelopeBuilder,
+    encoding: serialization.Encoding,
+    options: typing.Iterable[pkcs7.PKCS7Options],
+) -> bytes: ...
+def sign_and_serialize(
+    builder: pkcs7.PKCS7SignatureBuilder,
+    encoding: serialization.Encoding,
+    options: typing.Iterable[pkcs7.PKCS7Options],
+) -> bytes: ...
+def load_pem_pkcs7_certificates(
+    data: bytes,
+) -> list[x509.Certificate]: ...
+def load_der_pkcs7_certificates(
+    data: bytes,
+) -> list[x509.Certificate]: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..a53ee25dd7524fd3e449e9300e4b3e529a79a451
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi
@@ -0,0 +1,29 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography import x509
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.serialization import pkcs7
+
+class TestCertificate:
+    not_after_tag: int
+    not_before_tag: int
+    issuer_value_tags: list[int]
+    subject_value_tags: list[int]
+
+def test_parse_certificate(data: bytes) -> TestCertificate: ...
+def pkcs7_decrypt(
+    encoding: serialization.Encoding,
+    msg: bytes,
+    pkey: serialization.pkcs7.PKCS7PrivateKeyTypes,
+    cert_recipient: x509.Certificate,
+    options: list[pkcs7.PKCS7Options],
+) -> bytes: ...
+def pkcs7_verify(
+    encoding: serialization.Encoding,
+    sig: bytes,
+    msg: bytes | None,
+    certs: list[x509.Certificate],
+    options: list[pkcs7.PKCS7Options],
+) -> None: ...
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..aa85657fcfd8e6a53fb79d4c2cc0550378aaae0a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi
@@ -0,0 +1,108 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+import datetime
+import typing
+
+from cryptography import x509
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15
+from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
+
+def load_pem_x509_certificate(
+    data: bytes, backend: typing.Any = None
+) -> x509.Certificate: ...
+def load_der_x509_certificate(
+    data: bytes, backend: typing.Any = None
+) -> x509.Certificate: ...
+def load_pem_x509_certificates(
+    data: bytes,
+) -> list[x509.Certificate]: ...
+def load_pem_x509_crl(
+    data: bytes, backend: typing.Any = None
+) -> x509.CertificateRevocationList: ...
+def load_der_x509_crl(
+    data: bytes, backend: typing.Any = None
+) -> x509.CertificateRevocationList: ...
+def load_pem_x509_csr(
+    data: bytes, backend: typing.Any = None
+) -> x509.CertificateSigningRequest: ...
+def load_der_x509_csr(
+    data: bytes, backend: typing.Any = None
+) -> x509.CertificateSigningRequest: ...
+def encode_name_bytes(name: x509.Name) -> bytes: ...
+def encode_extension_value(extension: x509.ExtensionType) -> bytes: ...
+def create_x509_certificate(
+    builder: x509.CertificateBuilder,
+    private_key: PrivateKeyTypes,
+    hash_algorithm: hashes.HashAlgorithm | None,
+    rsa_padding: PKCS1v15 | PSS | None,
+) -> x509.Certificate: ...
+def create_x509_csr(
+    builder: x509.CertificateSigningRequestBuilder,
+    private_key: PrivateKeyTypes,
+    hash_algorithm: hashes.HashAlgorithm | None,
+    rsa_padding: PKCS1v15 | PSS | None,
+) -> x509.CertificateSigningRequest: ...
+def create_x509_crl(
+    builder: x509.CertificateRevocationListBuilder,
+    private_key: PrivateKeyTypes,
+    hash_algorithm: hashes.HashAlgorithm | None,
+    rsa_padding: PKCS1v15 | PSS | None,
+) -> x509.CertificateRevocationList: ...
+
+class Sct: ...
+class Certificate: ...
+class RevokedCertificate: ...
+class CertificateRevocationList: ...
+class CertificateSigningRequest: ...
+
+class PolicyBuilder:
+    def time(self, new_time: datetime.datetime) -> PolicyBuilder: ...
+    def store(self, new_store: Store) -> PolicyBuilder: ...
+    def max_chain_depth(self, new_max_chain_depth: int) -> PolicyBuilder: ...
+    def build_client_verifier(self) -> ClientVerifier: ...
+    def build_server_verifier(
+        self, subject: x509.verification.Subject
+    ) -> ServerVerifier: ...
+
+class VerifiedClient:
+    @property
+    def subjects(self) -> list[x509.GeneralName]: ...
+    @property
+    def chain(self) -> list[x509.Certificate]: ...
+
+class ClientVerifier:
+    @property
+    def validation_time(self) -> datetime.datetime: ...
+    @property
+    def store(self) -> Store: ...
+    @property
+    def max_chain_depth(self) -> int: ...
+    def verify(
+        self,
+        leaf: x509.Certificate,
+        intermediates: list[x509.Certificate],
+    ) -> VerifiedClient: ...
+
+class ServerVerifier:
+    @property
+    def subject(self) -> x509.verification.Subject: ...
+    @property
+    def validation_time(self) -> datetime.datetime: ...
+    @property
+    def store(self) -> Store: ...
+    @property
+    def max_chain_depth(self) -> int: ...
+    def verify(
+        self,
+        leaf: x509.Certificate,
+        intermediates: list[x509.Certificate],
+    ) -> list[x509.Certificate]: ...
+
+class Store:
+    def __init__(self, certs: list[x509.Certificate]) -> None: ...
+
+class VerificationError(Exception):
+    pass
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b509336233c2fafe4185a49da5909c8bbb38dfd7
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/__init__.py
@@ -0,0 +1,3 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py
new file mode 100644
index 0000000000000000000000000000000000000000..73c06f7d08ce78610a77e7f702ede6de54011334
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py
@@ -0,0 +1,183 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+
+def cryptography_has_set_cert_cb() -> list[str]:
+    return [
+        "SSL_CTX_set_cert_cb",
+        "SSL_set_cert_cb",
+    ]
+
+
+def cryptography_has_ssl_st() -> list[str]:
+    return [
+        "SSL_ST_BEFORE",
+        "SSL_ST_OK",
+        "SSL_ST_INIT",
+        "SSL_ST_RENEGOTIATE",
+    ]
+
+
+def cryptography_has_tls_st() -> list[str]:
+    return [
+        "TLS_ST_BEFORE",
+        "TLS_ST_OK",
+    ]
+
+
+def cryptography_has_ssl_sigalgs() -> list[str]:
+    return [
+        "SSL_CTX_set1_sigalgs_list",
+    ]
+
+
+def cryptography_has_psk() -> list[str]:
+    return [
+        "SSL_CTX_use_psk_identity_hint",
+        "SSL_CTX_set_psk_server_callback",
+        "SSL_CTX_set_psk_client_callback",
+    ]
+
+
+def cryptography_has_psk_tlsv13() -> list[str]:
+    return [
+        "SSL_CTX_set_psk_find_session_callback",
+        "SSL_CTX_set_psk_use_session_callback",
+        "Cryptography_SSL_SESSION_new",
+        "SSL_CIPHER_find",
+        "SSL_SESSION_set1_master_key",
+        "SSL_SESSION_set_cipher",
+        "SSL_SESSION_set_protocol_version",
+    ]
+
+
+def cryptography_has_custom_ext() -> list[str]:
+    return [
+        "SSL_CTX_add_client_custom_ext",
+        "SSL_CTX_add_server_custom_ext",
+        "SSL_extension_supported",
+    ]
+
+
+def cryptography_has_tlsv13_functions() -> list[str]:
+    return [
+        "SSL_VERIFY_POST_HANDSHAKE",
+        "SSL_CTX_set_ciphersuites",
+        "SSL_verify_client_post_handshake",
+        "SSL_CTX_set_post_handshake_auth",
+        "SSL_set_post_handshake_auth",
+        "SSL_SESSION_get_max_early_data",
+        "SSL_write_early_data",
+        "SSL_read_early_data",
+        "SSL_CTX_set_max_early_data",
+    ]
+
+
+def cryptography_has_engine() -> list[str]:
+    return [
+        "ENGINE_by_id",
+        "ENGINE_init",
+        "ENGINE_finish",
+        "ENGINE_get_default_RAND",
+        "ENGINE_set_default_RAND",
+        "ENGINE_unregister_RAND",
+        "ENGINE_ctrl_cmd",
+        "ENGINE_free",
+        "ENGINE_get_name",
+        "ENGINE_ctrl_cmd_string",
+        "ENGINE_load_builtin_engines",
+        "ENGINE_load_private_key",
+        "ENGINE_load_public_key",
+        "SSL_CTX_set_client_cert_engine",
+    ]
+
+
+def cryptography_has_verified_chain() -> list[str]:
+    return [
+        "SSL_get0_verified_chain",
+    ]
+
+
+def cryptography_has_srtp() -> list[str]:
+    return [
+        "SSL_CTX_set_tlsext_use_srtp",
+        "SSL_set_tlsext_use_srtp",
+        "SSL_get_selected_srtp_profile",
+    ]
+
+
+def cryptography_has_op_no_renegotiation() -> list[str]:
+    return [
+        "SSL_OP_NO_RENEGOTIATION",
+    ]
+
+
+def cryptography_has_dtls_get_data_mtu() -> list[str]:
+    return [
+        "DTLS_get_data_mtu",
+    ]
+
+
+def cryptography_has_ssl_cookie() -> list[str]:
+    return [
+        "SSL_OP_COOKIE_EXCHANGE",
+        "DTLSv1_listen",
+        "SSL_CTX_set_cookie_generate_cb",
+        "SSL_CTX_set_cookie_verify_cb",
+    ]
+
+
+def cryptography_has_prime_checks() -> list[str]:
+    return [
+        "BN_prime_checks_for_size",
+    ]
+
+
+def cryptography_has_unexpected_eof_while_reading() -> list[str]:
+    return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"]
+
+
+def cryptography_has_ssl_op_ignore_unexpected_eof() -> list[str]:
+    return [
+        "SSL_OP_IGNORE_UNEXPECTED_EOF",
+    ]
+
+
+def cryptography_has_get_extms_support() -> list[str]:
+    return ["SSL_get_extms_support"]
+
+
+# This is a mapping of
+# {condition: function-returning-names-dependent-on-that-condition} so we can
+# loop over them and delete unsupported names at runtime. It will be removed
+# when cffi supports #if in cdef. We use functions instead of just a dict of
+# lists so we can use coverage to measure which are used.
+CONDITIONAL_NAMES = {
+    "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
+    "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
+    "Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
+    "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs,
+    "Cryptography_HAS_PSK": cryptography_has_psk,
+    "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13,
+    "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext,
+    "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions,
+    "Cryptography_HAS_ENGINE": cryptography_has_engine,
+    "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain,
+    "Cryptography_HAS_SRTP": cryptography_has_srtp,
+    "Cryptography_HAS_OP_NO_RENEGOTIATION": (
+        cryptography_has_op_no_renegotiation
+    ),
+    "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu,
+    "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie,
+    "Cryptography_HAS_PRIME_CHECKS": cryptography_has_prime_checks,
+    "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": (
+        cryptography_has_unexpected_eof_while_reading
+    ),
+    "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": (
+        cryptography_has_ssl_op_ignore_unexpected_eof
+    ),
+    "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support,
+}
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/binding.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4dfeef485d1cd08ac18093019bb864f18e8ab38
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/bindings/openssl/binding.py
@@ -0,0 +1,121 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import os
+import sys
+import threading
+import types
+import typing
+import warnings
+
+import cryptography
+from cryptography.exceptions import InternalError
+from cryptography.hazmat.bindings._rust import _openssl, openssl
+from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
+
+
+def _openssl_assert(ok: bool) -> None:
+    if not ok:
+        errors = openssl.capture_error_stack()
+
+        raise InternalError(
+            "Unknown OpenSSL error. This error is commonly encountered when "
+            "another library is not cleaning up the OpenSSL error stack. If "
+            "you are using cryptography with another library that uses "
+            "OpenSSL try disabling it before reporting a bug. Otherwise "
+            "please file an issue at https://github.com/pyca/cryptography/"
+            "issues with information on how to reproduce "
+            f"this. ({errors!r})",
+            errors,
+        )
+
+
+def build_conditional_library(
+    lib: typing.Any,
+    conditional_names: dict[str, typing.Callable[[], list[str]]],
+) -> typing.Any:
+    conditional_lib = types.ModuleType("lib")
+    conditional_lib._original_lib = lib  # type: ignore[attr-defined]
+    excluded_names = set()
+    for condition, names_cb in conditional_names.items():
+        if not getattr(lib, condition):
+            excluded_names.update(names_cb())
+
+    for attr in dir(lib):
+        if attr not in excluded_names:
+            setattr(conditional_lib, attr, getattr(lib, attr))
+
+    return conditional_lib
+
+
+class Binding:
+    """
+    OpenSSL API wrapper.
+    """
+
+    lib: typing.ClassVar = None
+    ffi = _openssl.ffi
+    _lib_loaded = False
+    _init_lock = threading.Lock()
+
+    def __init__(self) -> None:
+        self._ensure_ffi_initialized()
+
+    @classmethod
+    def _ensure_ffi_initialized(cls) -> None:
+        with cls._init_lock:
+            if not cls._lib_loaded:
+                cls.lib = build_conditional_library(
+                    _openssl.lib, CONDITIONAL_NAMES
+                )
+                cls._lib_loaded = True
+
+    @classmethod
+    def init_static_locks(cls) -> None:
+        cls._ensure_ffi_initialized()
+
+
+def _verify_package_version(version: str) -> None:
+    # Occasionally we run into situations where the version of the Python
+    # package does not match the version of the shared object that is loaded.
+    # This may occur in environments where multiple versions of cryptography
+    # are installed and available in the python path. To avoid errors cropping
+    # up later this code checks that the currently imported package and the
+    # shared object that were loaded have the same version and raise an
+    # ImportError if they do not
+    so_package_version = _openssl.ffi.string(
+        _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION
+    )
+    if version.encode("ascii") != so_package_version:
+        raise ImportError(
+            "The version of cryptography does not match the loaded "
+            "shared object. This can happen if you have multiple copies of "
+            "cryptography installed in your Python path. Please try creating "
+            "a new virtual environment to resolve this issue. "
+            f"Loaded python version: {version}, "
+            f"shared object version: {so_package_version}"
+        )
+
+    _openssl_assert(
+        _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(),
+    )
+
+
+_verify_package_version(cryptography.__version__)
+
+Binding.init_static_locks()
+
+if (
+    sys.platform == "win32"
+    and os.environ.get("PROCESSOR_ARCHITEW6432") is not None
+):
+    warnings.warn(
+        "You are using cryptography on a 32-bit Python on a 64-bit Windows "
+        "Operating System. Cryptography will be significantly faster if you "
+        "switch to using a 64-bit Python.",
+        UserWarning,
+        stacklevel=2,
+    )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..41d731863aa2927b7a4fb02d7e536c35c678a102
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/__init__.py
@@ -0,0 +1,5 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..41d731863aa2927b7a4fb02d7e536c35c678a102
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py
@@ -0,0 +1,5 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7d4aa3c5d87ae867a271c57e3342e317a882dc1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py
@@ -0,0 +1,107 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.primitives._cipheralgorithm import (
+    BlockCipherAlgorithm,
+    CipherAlgorithm,
+    _verify_key_size,
+)
+
+
+class ARC4(CipherAlgorithm):
+    name = "RC4"
+    key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class TripleDES(BlockCipherAlgorithm):
+    name = "3DES"
+    block_size = 64
+    key_sizes = frozenset([64, 128, 192])
+
+    def __init__(self, key: bytes):
+        if len(key) == 8:
+            key += key + key
+        elif len(key) == 16:
+            key += key[:8]
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class Blowfish(BlockCipherAlgorithm):
+    name = "Blowfish"
+    block_size = 64
+    key_sizes = frozenset(range(32, 449, 8))
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class CAST5(BlockCipherAlgorithm):
+    name = "CAST5"
+    block_size = 64
+    key_sizes = frozenset(range(40, 129, 8))
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class SEED(BlockCipherAlgorithm):
+    name = "SEED"
+    block_size = 128
+    key_sizes = frozenset([128])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class IDEA(BlockCipherAlgorithm):
+    name = "IDEA"
+    block_size = 64
+    key_sizes = frozenset([128])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+# This class only allows RC2 with a 128-bit key. No support for
+# effective key bits or other key sizes is provided.
+class RC2(BlockCipherAlgorithm):
+    name = "RC2"
+    block_size = 64
+    key_sizes = frozenset([128])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b509336233c2fafe4185a49da5909c8bbb38dfd7
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/__init__.py
@@ -0,0 +1,3 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_asymmetric.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea55ffdf1a721f8fd2de8ae67de913bc47cbf55d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_asymmetric.py
@@ -0,0 +1,19 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+# This exists to break an import cycle. It is normally accessible from the
+# asymmetric padding module.
+
+
+class AsymmetricPadding(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def name(self) -> str:
+        """
+        A string naming this padding (e.g. "PSS", "PKCS1").
+        """
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py
new file mode 100644
index 0000000000000000000000000000000000000000..588a61698fdc2f733e74880dde4d2cb07183d2fa
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py
@@ -0,0 +1,58 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography import utils
+
+# This exists to break an import cycle. It is normally accessible from the
+# ciphers module.
+
+
+class CipherAlgorithm(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def name(self) -> str:
+        """
+        A string naming this mode (e.g. "AES", "Camellia").
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_sizes(self) -> frozenset[int]:
+        """
+        Valid key sizes for this algorithm in bits
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The size of the key being used as an integer in bits (e.g. 128, 256).
+        """
+
+
+class BlockCipherAlgorithm(CipherAlgorithm):
+    key: bytes
+
+    @property
+    @abc.abstractmethod
+    def block_size(self) -> int:
+        """
+        The size of a block as an integer in bits (e.g. 64, 128).
+        """
+
+
+def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes:
+    # Verify that the key is instance of bytes
+    utils._check_byteslike("key", key)
+
+    # Verify that the key size matches the expected key size
+    if len(key) * 8 not in algorithm.key_sizes:
+        raise ValueError(
+            f"Invalid key size ({len(key) * 8}) for {algorithm.name}."
+        )
+    return key
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_serialization.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_serialization.py
new file mode 100644
index 0000000000000000000000000000000000000000..46157721970be9b475ba0cef932be142f1f2733c
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/_serialization.py
@@ -0,0 +1,169 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography import utils
+from cryptography.hazmat.primitives.hashes import HashAlgorithm
+
+# This exists to break an import cycle. These classes are normally accessible
+# from the serialization module.
+
+
+class PBES(utils.Enum):
+    PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES"
+    PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC"
+
+
+class Encoding(utils.Enum):
+    PEM = "PEM"
+    DER = "DER"
+    OpenSSH = "OpenSSH"
+    Raw = "Raw"
+    X962 = "ANSI X9.62"
+    SMIME = "S/MIME"
+
+
+class PrivateFormat(utils.Enum):
+    PKCS8 = "PKCS8"
+    TraditionalOpenSSL = "TraditionalOpenSSL"
+    Raw = "Raw"
+    OpenSSH = "OpenSSH"
+    PKCS12 = "PKCS12"
+
+    def encryption_builder(self) -> KeySerializationEncryptionBuilder:
+        if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12):
+            raise ValueError(
+                "encryption_builder only supported with PrivateFormat.OpenSSH"
+                " and PrivateFormat.PKCS12"
+            )
+        return KeySerializationEncryptionBuilder(self)
+
+
+class PublicFormat(utils.Enum):
+    SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
+    PKCS1 = "Raw PKCS#1"
+    OpenSSH = "OpenSSH"
+    Raw = "Raw"
+    CompressedPoint = "X9.62 Compressed Point"
+    UncompressedPoint = "X9.62 Uncompressed Point"
+
+
+class ParameterFormat(utils.Enum):
+    PKCS3 = "PKCS3"
+
+
+class KeySerializationEncryption(metaclass=abc.ABCMeta):
+    pass
+
+
+class BestAvailableEncryption(KeySerializationEncryption):
+    def __init__(self, password: bytes):
+        if not isinstance(password, bytes) or len(password) == 0:
+            raise ValueError("Password must be 1 or more bytes.")
+
+        self.password = password
+
+
+class NoEncryption(KeySerializationEncryption):
+    pass
+
+
+class KeySerializationEncryptionBuilder:
+    def __init__(
+        self,
+        format: PrivateFormat,
+        *,
+        _kdf_rounds: int | None = None,
+        _hmac_hash: HashAlgorithm | None = None,
+        _key_cert_algorithm: PBES | None = None,
+    ) -> None:
+        self._format = format
+
+        self._kdf_rounds = _kdf_rounds
+        self._hmac_hash = _hmac_hash
+        self._key_cert_algorithm = _key_cert_algorithm
+
+    def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder:
+        if self._kdf_rounds is not None:
+            raise ValueError("kdf_rounds already set")
+
+        if not isinstance(rounds, int):
+            raise TypeError("kdf_rounds must be an integer")
+
+        if rounds < 1:
+            raise ValueError("kdf_rounds must be a positive integer")
+
+        return KeySerializationEncryptionBuilder(
+            self._format,
+            _kdf_rounds=rounds,
+            _hmac_hash=self._hmac_hash,
+            _key_cert_algorithm=self._key_cert_algorithm,
+        )
+
+    def hmac_hash(
+        self, algorithm: HashAlgorithm
+    ) -> KeySerializationEncryptionBuilder:
+        if self._format is not PrivateFormat.PKCS12:
+            raise TypeError(
+                "hmac_hash only supported with PrivateFormat.PKCS12"
+            )
+
+        if self._hmac_hash is not None:
+            raise ValueError("hmac_hash already set")
+        return KeySerializationEncryptionBuilder(
+            self._format,
+            _kdf_rounds=self._kdf_rounds,
+            _hmac_hash=algorithm,
+            _key_cert_algorithm=self._key_cert_algorithm,
+        )
+
+    def key_cert_algorithm(
+        self, algorithm: PBES
+    ) -> KeySerializationEncryptionBuilder:
+        if self._format is not PrivateFormat.PKCS12:
+            raise TypeError(
+                "key_cert_algorithm only supported with "
+                "PrivateFormat.PKCS12"
+            )
+        if self._key_cert_algorithm is not None:
+            raise ValueError("key_cert_algorithm already set")
+        return KeySerializationEncryptionBuilder(
+            self._format,
+            _kdf_rounds=self._kdf_rounds,
+            _hmac_hash=self._hmac_hash,
+            _key_cert_algorithm=algorithm,
+        )
+
+    def build(self, password: bytes) -> KeySerializationEncryption:
+        if not isinstance(password, bytes) or len(password) == 0:
+            raise ValueError("Password must be 1 or more bytes.")
+
+        return _KeySerializationEncryption(
+            self._format,
+            password,
+            kdf_rounds=self._kdf_rounds,
+            hmac_hash=self._hmac_hash,
+            key_cert_algorithm=self._key_cert_algorithm,
+        )
+
+
+class _KeySerializationEncryption(KeySerializationEncryption):
+    def __init__(
+        self,
+        format: PrivateFormat,
+        password: bytes,
+        *,
+        kdf_rounds: int | None,
+        hmac_hash: HashAlgorithm | None,
+        key_cert_algorithm: PBES | None,
+    ):
+        self._format = format
+        self.password = password
+
+        self._kdf_rounds = kdf_rounds
+        self._hmac_hash = hmac_hash
+        self._key_cert_algorithm = key_cert_algorithm
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b509336233c2fafe4185a49da5909c8bbb38dfd7
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py
@@ -0,0 +1,3 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py
new file mode 100644
index 0000000000000000000000000000000000000000..31c9748a91cdf891d436a0c68448dc2bf7f005eb
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py
@@ -0,0 +1,135 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization
+
+generate_parameters = rust_openssl.dh.generate_parameters
+
+
+DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers
+DHPublicNumbers = rust_openssl.dh.DHPublicNumbers
+DHParameterNumbers = rust_openssl.dh.DHParameterNumbers
+
+
+class DHParameters(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def generate_private_key(self) -> DHPrivateKey:
+        """
+        Generates and returns a DHPrivateKey.
+        """
+
+    @abc.abstractmethod
+    def parameter_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.ParameterFormat,
+    ) -> bytes:
+        """
+        Returns the parameters serialized as bytes.
+        """
+
+    @abc.abstractmethod
+    def parameter_numbers(self) -> DHParameterNumbers:
+        """
+        Returns a DHParameterNumbers.
+        """
+
+
+DHParametersWithSerialization = DHParameters
+DHParameters.register(rust_openssl.dh.DHParameters)
+
+
+class DHPublicKey(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The bit length of the prime modulus.
+        """
+
+    @abc.abstractmethod
+    def parameters(self) -> DHParameters:
+        """
+        The DHParameters object associated with this public key.
+        """
+
+    @abc.abstractmethod
+    def public_numbers(self) -> DHPublicNumbers:
+        """
+        Returns a DHPublicNumbers.
+        """
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+DHPublicKeyWithSerialization = DHPublicKey
+DHPublicKey.register(rust_openssl.dh.DHPublicKey)
+
+
+class DHPrivateKey(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The bit length of the prime modulus.
+        """
+
+    @abc.abstractmethod
+    def public_key(self) -> DHPublicKey:
+        """
+        The DHPublicKey associated with this private key.
+        """
+
+    @abc.abstractmethod
+    def parameters(self) -> DHParameters:
+        """
+        The DHParameters object associated with this private key.
+        """
+
+    @abc.abstractmethod
+    def exchange(self, peer_public_key: DHPublicKey) -> bytes:
+        """
+        Given peer's DHPublicKey, carry out the key exchange and
+        return shared key as bytes.
+        """
+
+    @abc.abstractmethod
+    def private_numbers(self) -> DHPrivateNumbers:
+        """
+        Returns a DHPrivateNumbers.
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+
+DHPrivateKeyWithSerialization = DHPrivateKey
+DHPrivateKey.register(rust_openssl.dh.DHPrivateKey)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py
new file mode 100644
index 0000000000000000000000000000000000000000..6dd34c0e09b00554f257cdc7f628e6f3db9ed82d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -0,0 +1,154 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import typing
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization, hashes
+from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
+
+
+class DSAParameters(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def generate_private_key(self) -> DSAPrivateKey:
+        """
+        Generates and returns a DSAPrivateKey.
+        """
+
+    @abc.abstractmethod
+    def parameter_numbers(self) -> DSAParameterNumbers:
+        """
+        Returns a DSAParameterNumbers.
+        """
+
+
+DSAParametersWithNumbers = DSAParameters
+DSAParameters.register(rust_openssl.dsa.DSAParameters)
+
+
+class DSAPrivateKey(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The bit length of the prime modulus.
+        """
+
+    @abc.abstractmethod
+    def public_key(self) -> DSAPublicKey:
+        """
+        The DSAPublicKey associated with this private key.
+        """
+
+    @abc.abstractmethod
+    def parameters(self) -> DSAParameters:
+        """
+        The DSAParameters object associated with this private key.
+        """
+
+    @abc.abstractmethod
+    def sign(
+        self,
+        data: bytes,
+        algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
+    ) -> bytes:
+        """
+        Signs the data
+        """
+
+    @abc.abstractmethod
+    def private_numbers(self) -> DSAPrivateNumbers:
+        """
+        Returns a DSAPrivateNumbers.
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+
+DSAPrivateKeyWithSerialization = DSAPrivateKey
+DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey)
+
+
+class DSAPublicKey(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The bit length of the prime modulus.
+        """
+
+    @abc.abstractmethod
+    def parameters(self) -> DSAParameters:
+        """
+        The DSAParameters object associated with this public key.
+        """
+
+    @abc.abstractmethod
+    def public_numbers(self) -> DSAPublicNumbers:
+        """
+        Returns a DSAPublicNumbers.
+        """
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+    @abc.abstractmethod
+    def verify(
+        self,
+        signature: bytes,
+        data: bytes,
+        algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
+    ) -> None:
+        """
+        Verifies the signature of the data.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+DSAPublicKeyWithSerialization = DSAPublicKey
+DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey)
+
+DSAPrivateNumbers = rust_openssl.dsa.DSAPrivateNumbers
+DSAPublicNumbers = rust_openssl.dsa.DSAPublicNumbers
+DSAParameterNumbers = rust_openssl.dsa.DSAParameterNumbers
+
+
+def generate_parameters(
+    key_size: int, backend: typing.Any = None
+) -> DSAParameters:
+    if key_size not in (1024, 2048, 3072, 4096):
+        raise ValueError("Key size must be 1024, 2048, 3072, or 4096 bits.")
+
+    return rust_openssl.dsa.generate_parameters(key_size)
+
+
+def generate_private_key(
+    key_size: int, backend: typing.Any = None
+) -> DSAPrivateKey:
+    parameters = generate_parameters(key_size)
+    return parameters.generate_private_key()
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py
new file mode 100644
index 0000000000000000000000000000000000000000..da1fbea13a6e14564e63e780a935a84ad5b58f80
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -0,0 +1,403 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat._oid import ObjectIdentifier
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization, hashes
+from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
+
+
+class EllipticCurveOID:
+    SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1")
+    SECP224R1 = ObjectIdentifier("1.3.132.0.33")
+    SECP256K1 = ObjectIdentifier("1.3.132.0.10")
+    SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7")
+    SECP384R1 = ObjectIdentifier("1.3.132.0.34")
+    SECP521R1 = ObjectIdentifier("1.3.132.0.35")
+    BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7")
+    BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11")
+    BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13")
+    SECT163K1 = ObjectIdentifier("1.3.132.0.1")
+    SECT163R2 = ObjectIdentifier("1.3.132.0.15")
+    SECT233K1 = ObjectIdentifier("1.3.132.0.26")
+    SECT233R1 = ObjectIdentifier("1.3.132.0.27")
+    SECT283K1 = ObjectIdentifier("1.3.132.0.16")
+    SECT283R1 = ObjectIdentifier("1.3.132.0.17")
+    SECT409K1 = ObjectIdentifier("1.3.132.0.36")
+    SECT409R1 = ObjectIdentifier("1.3.132.0.37")
+    SECT571K1 = ObjectIdentifier("1.3.132.0.38")
+    SECT571R1 = ObjectIdentifier("1.3.132.0.39")
+
+
+class EllipticCurve(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def name(self) -> str:
+        """
+        The name of the curve. e.g. secp256r1.
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        Bit size of a secret scalar for the curve.
+        """
+
+
+class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def algorithm(
+        self,
+    ) -> asym_utils.Prehashed | hashes.HashAlgorithm:
+        """
+        The digest algorithm used with this signature.
+        """
+
+
+class EllipticCurvePrivateKey(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def exchange(
+        self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey
+    ) -> bytes:
+        """
+        Performs a key exchange operation using the provided algorithm with the
+        provided peer's public key.
+        """
+
+    @abc.abstractmethod
+    def public_key(self) -> EllipticCurvePublicKey:
+        """
+        The EllipticCurvePublicKey for this private key.
+        """
+
+    @property
+    @abc.abstractmethod
+    def curve(self) -> EllipticCurve:
+        """
+        The EllipticCurve that this key is on.
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        Bit size of a secret scalar for the curve.
+        """
+
+    @abc.abstractmethod
+    def sign(
+        self,
+        data: bytes,
+        signature_algorithm: EllipticCurveSignatureAlgorithm,
+    ) -> bytes:
+        """
+        Signs the data
+        """
+
+    @abc.abstractmethod
+    def private_numbers(self) -> EllipticCurvePrivateNumbers:
+        """
+        Returns an EllipticCurvePrivateNumbers.
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+
+EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey
+EllipticCurvePrivateKey.register(rust_openssl.ec.ECPrivateKey)
+
+
+class EllipticCurvePublicKey(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def curve(self) -> EllipticCurve:
+        """
+        The EllipticCurve that this key is on.
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        Bit size of a secret scalar for the curve.
+        """
+
+    @abc.abstractmethod
+    def public_numbers(self) -> EllipticCurvePublicNumbers:
+        """
+        Returns an EllipticCurvePublicNumbers.
+        """
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+    @abc.abstractmethod
+    def verify(
+        self,
+        signature: bytes,
+        data: bytes,
+        signature_algorithm: EllipticCurveSignatureAlgorithm,
+    ) -> None:
+        """
+        Verifies the signature of the data.
+        """
+
+    @classmethod
+    def from_encoded_point(
+        cls, curve: EllipticCurve, data: bytes
+    ) -> EllipticCurvePublicKey:
+        utils._check_bytes("data", data)
+
+        if len(data) == 0:
+            raise ValueError("data must not be an empty byte string")
+
+        if data[0] not in [0x02, 0x03, 0x04]:
+            raise ValueError("Unsupported elliptic curve point type")
+
+        return rust_openssl.ec.from_public_bytes(curve, data)
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey
+EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey)
+
+EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers
+EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers
+
+
+class SECT571R1(EllipticCurve):
+    name = "sect571r1"
+    key_size = 570
+
+
+class SECT409R1(EllipticCurve):
+    name = "sect409r1"
+    key_size = 409
+
+
+class SECT283R1(EllipticCurve):
+    name = "sect283r1"
+    key_size = 283
+
+
+class SECT233R1(EllipticCurve):
+    name = "sect233r1"
+    key_size = 233
+
+
+class SECT163R2(EllipticCurve):
+    name = "sect163r2"
+    key_size = 163
+
+
+class SECT571K1(EllipticCurve):
+    name = "sect571k1"
+    key_size = 571
+
+
+class SECT409K1(EllipticCurve):
+    name = "sect409k1"
+    key_size = 409
+
+
+class SECT283K1(EllipticCurve):
+    name = "sect283k1"
+    key_size = 283
+
+
+class SECT233K1(EllipticCurve):
+    name = "sect233k1"
+    key_size = 233
+
+
+class SECT163K1(EllipticCurve):
+    name = "sect163k1"
+    key_size = 163
+
+
+class SECP521R1(EllipticCurve):
+    name = "secp521r1"
+    key_size = 521
+
+
+class SECP384R1(EllipticCurve):
+    name = "secp384r1"
+    key_size = 384
+
+
+class SECP256R1(EllipticCurve):
+    name = "secp256r1"
+    key_size = 256
+
+
+class SECP256K1(EllipticCurve):
+    name = "secp256k1"
+    key_size = 256
+
+
+class SECP224R1(EllipticCurve):
+    name = "secp224r1"
+    key_size = 224
+
+
+class SECP192R1(EllipticCurve):
+    name = "secp192r1"
+    key_size = 192
+
+
+class BrainpoolP256R1(EllipticCurve):
+    name = "brainpoolP256r1"
+    key_size = 256
+
+
+class BrainpoolP384R1(EllipticCurve):
+    name = "brainpoolP384r1"
+    key_size = 384
+
+
+class BrainpoolP512R1(EllipticCurve):
+    name = "brainpoolP512r1"
+    key_size = 512
+
+
+_CURVE_TYPES: dict[str, EllipticCurve] = {
+    "prime192v1": SECP192R1(),
+    "prime256v1": SECP256R1(),
+    "secp192r1": SECP192R1(),
+    "secp224r1": SECP224R1(),
+    "secp256r1": SECP256R1(),
+    "secp384r1": SECP384R1(),
+    "secp521r1": SECP521R1(),
+    "secp256k1": SECP256K1(),
+    "sect163k1": SECT163K1(),
+    "sect233k1": SECT233K1(),
+    "sect283k1": SECT283K1(),
+    "sect409k1": SECT409K1(),
+    "sect571k1": SECT571K1(),
+    "sect163r2": SECT163R2(),
+    "sect233r1": SECT233R1(),
+    "sect283r1": SECT283R1(),
+    "sect409r1": SECT409R1(),
+    "sect571r1": SECT571R1(),
+    "brainpoolP256r1": BrainpoolP256R1(),
+    "brainpoolP384r1": BrainpoolP384R1(),
+    "brainpoolP512r1": BrainpoolP512R1(),
+}
+
+
+class ECDSA(EllipticCurveSignatureAlgorithm):
+    def __init__(
+        self,
+        algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
+        deterministic_signing: bool = False,
+    ):
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if (
+            deterministic_signing
+            and not backend.ecdsa_deterministic_supported()
+        ):
+            raise UnsupportedAlgorithm(
+                "ECDSA with deterministic signature (RFC 6979) is not "
+                "supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+        self._algorithm = algorithm
+        self._deterministic_signing = deterministic_signing
+
+    @property
+    def algorithm(
+        self,
+    ) -> asym_utils.Prehashed | hashes.HashAlgorithm:
+        return self._algorithm
+
+    @property
+    def deterministic_signing(
+        self,
+    ) -> bool:
+        return self._deterministic_signing
+
+
+generate_private_key = rust_openssl.ec.generate_private_key
+
+
+def derive_private_key(
+    private_value: int,
+    curve: EllipticCurve,
+    backend: typing.Any = None,
+) -> EllipticCurvePrivateKey:
+    if not isinstance(private_value, int):
+        raise TypeError("private_value must be an integer type.")
+
+    if private_value <= 0:
+        raise ValueError("private_value must be a positive integer.")
+
+    return rust_openssl.ec.derive_private_key(private_value, curve)
+
+
+class ECDH:
+    pass
+
+
+_OID_TO_CURVE = {
+    EllipticCurveOID.SECP192R1: SECP192R1,
+    EllipticCurveOID.SECP224R1: SECP224R1,
+    EllipticCurveOID.SECP256K1: SECP256K1,
+    EllipticCurveOID.SECP256R1: SECP256R1,
+    EllipticCurveOID.SECP384R1: SECP384R1,
+    EllipticCurveOID.SECP521R1: SECP521R1,
+    EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1,
+    EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1,
+    EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1,
+    EllipticCurveOID.SECT163K1: SECT163K1,
+    EllipticCurveOID.SECT163R2: SECT163R2,
+    EllipticCurveOID.SECT233K1: SECT233K1,
+    EllipticCurveOID.SECT233R1: SECT233R1,
+    EllipticCurveOID.SECT283K1: SECT283K1,
+    EllipticCurveOID.SECT283R1: SECT283R1,
+    EllipticCurveOID.SECT409K1: SECT409K1,
+    EllipticCurveOID.SECT409R1: SECT409R1,
+    EllipticCurveOID.SECT571K1: SECT571K1,
+    EllipticCurveOID.SECT571R1: SECT571R1,
+}
+
+
+def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]:
+    try:
+        return _OID_TO_CURVE[oid]
+    except KeyError:
+        raise LookupError(
+            "The provided object identifier has no matching elliptic "
+            "curve class"
+        )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a26185d7dbca401863a58a1e44178b2d4a1538f
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py
@@ -0,0 +1,116 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization
+
+
+class Ed25519PublicKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.ed25519_supported():
+            raise UnsupportedAlgorithm(
+                "ed25519 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+
+        return rust_openssl.ed25519.from_public_bytes(data)
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        The serialized bytes of the public key.
+        """
+
+    @abc.abstractmethod
+    def public_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the public key.
+        Equivalent to public_bytes(Raw, Raw).
+        """
+
+    @abc.abstractmethod
+    def verify(self, signature: bytes, data: bytes) -> None:
+        """
+        Verify the signature.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey)
+
+
+class Ed25519PrivateKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def generate(cls) -> Ed25519PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.ed25519_supported():
+            raise UnsupportedAlgorithm(
+                "ed25519 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+
+        return rust_openssl.ed25519.generate_key()
+
+    @classmethod
+    def from_private_bytes(cls, data: bytes) -> Ed25519PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.ed25519_supported():
+            raise UnsupportedAlgorithm(
+                "ed25519 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+
+        return rust_openssl.ed25519.from_private_bytes(data)
+
+    @abc.abstractmethod
+    def public_key(self) -> Ed25519PublicKey:
+        """
+        The Ed25519PublicKey derived from the private key.
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        The serialized bytes of the private key.
+        """
+
+    @abc.abstractmethod
+    def private_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the private key.
+        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
+        """
+
+    @abc.abstractmethod
+    def sign(self, data: bytes) -> bytes:
+        """
+        Signs the data.
+        """
+
+
+Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py
new file mode 100644
index 0000000000000000000000000000000000000000..78c82c4a3c4534aa1f1ac17e072e7788c105c9a7
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py
@@ -0,0 +1,118 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization
+
+
+class Ed448PublicKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def from_public_bytes(cls, data: bytes) -> Ed448PublicKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.ed448_supported():
+            raise UnsupportedAlgorithm(
+                "ed448 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+
+        return rust_openssl.ed448.from_public_bytes(data)
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        The serialized bytes of the public key.
+        """
+
+    @abc.abstractmethod
+    def public_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the public key.
+        Equivalent to public_bytes(Raw, Raw).
+        """
+
+    @abc.abstractmethod
+    def verify(self, signature: bytes, data: bytes) -> None:
+        """
+        Verify the signature.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+if hasattr(rust_openssl, "ed448"):
+    Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey)
+
+
+class Ed448PrivateKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def generate(cls) -> Ed448PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.ed448_supported():
+            raise UnsupportedAlgorithm(
+                "ed448 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+
+        return rust_openssl.ed448.generate_key()
+
+    @classmethod
+    def from_private_bytes(cls, data: bytes) -> Ed448PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.ed448_supported():
+            raise UnsupportedAlgorithm(
+                "ed448 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+            )
+
+        return rust_openssl.ed448.from_private_bytes(data)
+
+    @abc.abstractmethod
+    def public_key(self) -> Ed448PublicKey:
+        """
+        The Ed448PublicKey derived from the private key.
+        """
+
+    @abc.abstractmethod
+    def sign(self, data: bytes) -> bytes:
+        """
+        Signs the data.
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        The serialized bytes of the private key.
+        """
+
+    @abc.abstractmethod
+    def private_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the private key.
+        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
+        """
+
+
+if hasattr(rust_openssl, "x448"):
+    Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4babf44f79b1fa6ed1920d55c1d641e5904d6ab
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py
@@ -0,0 +1,113 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives._asymmetric import (
+    AsymmetricPadding as AsymmetricPadding,
+)
+from cryptography.hazmat.primitives.asymmetric import rsa
+
+
+class PKCS1v15(AsymmetricPadding):
+    name = "EMSA-PKCS1-v1_5"
+
+
+class _MaxLength:
+    "Sentinel value for `MAX_LENGTH`."
+
+
+class _Auto:
+    "Sentinel value for `AUTO`."
+
+
+class _DigestLength:
+    "Sentinel value for `DIGEST_LENGTH`."
+
+
+class PSS(AsymmetricPadding):
+    MAX_LENGTH = _MaxLength()
+    AUTO = _Auto()
+    DIGEST_LENGTH = _DigestLength()
+    name = "EMSA-PSS"
+    _salt_length: int | _MaxLength | _Auto | _DigestLength
+
+    def __init__(
+        self,
+        mgf: MGF,
+        salt_length: int | _MaxLength | _Auto | _DigestLength,
+    ) -> None:
+        self._mgf = mgf
+
+        if not isinstance(
+            salt_length, (int, _MaxLength, _Auto, _DigestLength)
+        ):
+            raise TypeError(
+                "salt_length must be an integer, MAX_LENGTH, "
+                "DIGEST_LENGTH, or AUTO"
+            )
+
+        if isinstance(salt_length, int) and salt_length < 0:
+            raise ValueError("salt_length must be zero or greater.")
+
+        self._salt_length = salt_length
+
+    @property
+    def mgf(self) -> MGF:
+        return self._mgf
+
+
+class OAEP(AsymmetricPadding):
+    name = "EME-OAEP"
+
+    def __init__(
+        self,
+        mgf: MGF,
+        algorithm: hashes.HashAlgorithm,
+        label: bytes | None,
+    ):
+        if not isinstance(algorithm, hashes.HashAlgorithm):
+            raise TypeError("Expected instance of hashes.HashAlgorithm.")
+
+        self._mgf = mgf
+        self._algorithm = algorithm
+        self._label = label
+
+    @property
+    def algorithm(self) -> hashes.HashAlgorithm:
+        return self._algorithm
+
+    @property
+    def mgf(self) -> MGF:
+        return self._mgf
+
+
+class MGF(metaclass=abc.ABCMeta):
+    _algorithm: hashes.HashAlgorithm
+
+
+class MGF1(MGF):
+    MAX_LENGTH = _MaxLength()
+
+    def __init__(self, algorithm: hashes.HashAlgorithm):
+        if not isinstance(algorithm, hashes.HashAlgorithm):
+            raise TypeError("Expected instance of hashes.HashAlgorithm.")
+
+        self._algorithm = algorithm
+
+
+def calculate_max_pss_salt_length(
+    key: rsa.RSAPrivateKey | rsa.RSAPublicKey,
+    hash_algorithm: hashes.HashAlgorithm,
+) -> int:
+    if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
+        raise TypeError("key must be an RSA public or private key")
+    # bit length - 1 per RFC 3447
+    emlen = (key.key_size + 6) // 8
+    salt_length = emlen - hash_algorithm.digest_size - 2
+    assert salt_length >= 0
+    return salt_length
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a387b5ea55de70fd02627e13a90de78146eb4ae
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -0,0 +1,260 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import typing
+from math import gcd
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization, hashes
+from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
+from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
+
+
+class RSAPrivateKey(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:
+        """
+        Decrypts the provided ciphertext.
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The bit length of the public modulus.
+        """
+
+    @abc.abstractmethod
+    def public_key(self) -> RSAPublicKey:
+        """
+        The RSAPublicKey associated with this private key.
+        """
+
+    @abc.abstractmethod
+    def sign(
+        self,
+        data: bytes,
+        padding: AsymmetricPadding,
+        algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
+    ) -> bytes:
+        """
+        Signs the data.
+        """
+
+    @abc.abstractmethod
+    def private_numbers(self) -> RSAPrivateNumbers:
+        """
+        Returns an RSAPrivateNumbers.
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+
+RSAPrivateKeyWithSerialization = RSAPrivateKey
+RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey)
+
+
+class RSAPublicKey(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:
+        """
+        Encrypts the given plaintext.
+        """
+
+    @property
+    @abc.abstractmethod
+    def key_size(self) -> int:
+        """
+        The bit length of the public modulus.
+        """
+
+    @abc.abstractmethod
+    def public_numbers(self) -> RSAPublicNumbers:
+        """
+        Returns an RSAPublicNumbers
+        """
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        Returns the key serialized as bytes.
+        """
+
+    @abc.abstractmethod
+    def verify(
+        self,
+        signature: bytes,
+        data: bytes,
+        padding: AsymmetricPadding,
+        algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
+    ) -> None:
+        """
+        Verifies the signature of the data.
+        """
+
+    @abc.abstractmethod
+    def recover_data_from_signature(
+        self,
+        signature: bytes,
+        padding: AsymmetricPadding,
+        algorithm: hashes.HashAlgorithm | None,
+    ) -> bytes:
+        """
+        Recovers the original data from the signature.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+RSAPublicKeyWithSerialization = RSAPublicKey
+RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey)
+
+RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers
+RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers
+
+
+def generate_private_key(
+    public_exponent: int,
+    key_size: int,
+    backend: typing.Any = None,
+) -> RSAPrivateKey:
+    _verify_rsa_parameters(public_exponent, key_size)
+    return rust_openssl.rsa.generate_private_key(public_exponent, key_size)
+
+
+def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None:
+    if public_exponent not in (3, 65537):
+        raise ValueError(
+            "public_exponent must be either 3 (for legacy compatibility) or "
+            "65537. Almost everyone should choose 65537 here!"
+        )
+
+    if key_size < 1024:
+        raise ValueError("key_size must be at least 1024-bits.")
+
+
+def _modinv(e: int, m: int) -> int:
+    """
+    Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1
+    """
+    x1, x2 = 1, 0
+    a, b = e, m
+    while b > 0:
+        q, r = divmod(a, b)
+        xn = x1 - q * x2
+        a, b, x1, x2 = b, r, x2, xn
+    return x1 % m
+
+
+def rsa_crt_iqmp(p: int, q: int) -> int:
+    """
+    Compute the CRT (q ** -1) % p value from RSA primes p and q.
+    """
+    return _modinv(q, p)
+
+
+def rsa_crt_dmp1(private_exponent: int, p: int) -> int:
+    """
+    Compute the CRT private_exponent % (p - 1) value from the RSA
+    private_exponent (d) and p.
+    """
+    return private_exponent % (p - 1)
+
+
+def rsa_crt_dmq1(private_exponent: int, q: int) -> int:
+    """
+    Compute the CRT private_exponent % (q - 1) value from the RSA
+    private_exponent (d) and q.
+    """
+    return private_exponent % (q - 1)
+
+
+def rsa_recover_private_exponent(e: int, p: int, q: int) -> int:
+    """
+    Compute the RSA private_exponent (d) given the public exponent (e)
+    and the RSA primes p and q.
+
+    This uses the Carmichael totient function to generate the
+    smallest possible working value of the private exponent.
+    """
+    # This lambda_n is the Carmichael totient function.
+    # The original RSA paper uses the Euler totient function
+    # here: phi_n = (p - 1) * (q - 1)
+    # Either version of the private exponent will work, but the
+    # one generated by the older formulation may be larger
+    # than necessary. (lambda_n always divides phi_n)
+    #
+    # TODO: Replace with lcm(p - 1, q - 1) once the minimum
+    # supported Python version is >= 3.9.
+    lambda_n = (p - 1) * (q - 1) // gcd(p - 1, q - 1)
+    return _modinv(e, lambda_n)
+
+
+# Controls the number of iterations rsa_recover_prime_factors will perform
+# to obtain the prime factors. Each iteration increments by 2 so the actual
+# maximum attempts is half this number.
+_MAX_RECOVERY_ATTEMPTS = 1000
+
+
+def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]:
+    """
+    Compute factors p and q from the private exponent d. We assume that n has
+    no more than two factors. This function is adapted from code in PyCrypto.
+    """
+    # See 8.2.2(i) in Handbook of Applied Cryptography.
+    ktot = d * e - 1
+    # The quantity d*e-1 is a multiple of phi(n), even,
+    # and can be represented as t*2^s.
+    t = ktot
+    while t % 2 == 0:
+        t = t // 2
+    # Cycle through all multiplicative inverses in Zn.
+    # The algorithm is non-deterministic, but there is a 50% chance
+    # any candidate a leads to successful factoring.
+    # See "Digitalized Signatures and Public Key Functions as Intractable
+    # as Factorization", M. Rabin, 1979
+    spotted = False
+    a = 2
+    while not spotted and a < _MAX_RECOVERY_ATTEMPTS:
+        k = t
+        # Cycle through all values a^{t*2^i}=a^k
+        while k < ktot:
+            cand = pow(a, k, n)
+            # Check if a^k is a non-trivial root of unity (mod n)
+            if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1:
+                # We have found a number such that (cand-1)(cand+1)=0 (mod n).
+                # Either of the terms divides n.
+                p = gcd(cand + 1, n)
+                spotted = True
+                break
+            k *= 2
+        # This value was not any good... let's try another!
+        a += 2
+    if not spotted:
+        raise ValueError("Unable to compute factors p and q from exponent d.")
+    # Found !
+    q, r = divmod(n, p)
+    assert r == 0
+    p, q = sorted((p, q), reverse=True)
+    return (p, q)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fe4eaf51d850c30ef7764d1c0bbf17533b8ad38
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/types.py
@@ -0,0 +1,111 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import utils
+from cryptography.hazmat.primitives.asymmetric import (
+    dh,
+    dsa,
+    ec,
+    ed448,
+    ed25519,
+    rsa,
+    x448,
+    x25519,
+)
+
+# Every asymmetric key type
+PublicKeyTypes = typing.Union[
+    dh.DHPublicKey,
+    dsa.DSAPublicKey,
+    rsa.RSAPublicKey,
+    ec.EllipticCurvePublicKey,
+    ed25519.Ed25519PublicKey,
+    ed448.Ed448PublicKey,
+    x25519.X25519PublicKey,
+    x448.X448PublicKey,
+]
+PUBLIC_KEY_TYPES = PublicKeyTypes
+utils.deprecated(
+    PUBLIC_KEY_TYPES,
+    __name__,
+    "Use PublicKeyTypes instead",
+    utils.DeprecatedIn40,
+    name="PUBLIC_KEY_TYPES",
+)
+# Every asymmetric key type
+PrivateKeyTypes = typing.Union[
+    dh.DHPrivateKey,
+    ed25519.Ed25519PrivateKey,
+    ed448.Ed448PrivateKey,
+    rsa.RSAPrivateKey,
+    dsa.DSAPrivateKey,
+    ec.EllipticCurvePrivateKey,
+    x25519.X25519PrivateKey,
+    x448.X448PrivateKey,
+]
+PRIVATE_KEY_TYPES = PrivateKeyTypes
+utils.deprecated(
+    PRIVATE_KEY_TYPES,
+    __name__,
+    "Use PrivateKeyTypes instead",
+    utils.DeprecatedIn40,
+    name="PRIVATE_KEY_TYPES",
+)
+# Just the key types we allow to be used for x509 signing. This mirrors
+# the certificate public key types
+CertificateIssuerPrivateKeyTypes = typing.Union[
+    ed25519.Ed25519PrivateKey,
+    ed448.Ed448PrivateKey,
+    rsa.RSAPrivateKey,
+    dsa.DSAPrivateKey,
+    ec.EllipticCurvePrivateKey,
+]
+CERTIFICATE_PRIVATE_KEY_TYPES = CertificateIssuerPrivateKeyTypes
+utils.deprecated(
+    CERTIFICATE_PRIVATE_KEY_TYPES,
+    __name__,
+    "Use CertificateIssuerPrivateKeyTypes instead",
+    utils.DeprecatedIn40,
+    name="CERTIFICATE_PRIVATE_KEY_TYPES",
+)
+# Just the key types we allow to be used for x509 signing. This mirrors
+# the certificate private key types
+CertificateIssuerPublicKeyTypes = typing.Union[
+    dsa.DSAPublicKey,
+    rsa.RSAPublicKey,
+    ec.EllipticCurvePublicKey,
+    ed25519.Ed25519PublicKey,
+    ed448.Ed448PublicKey,
+]
+CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = CertificateIssuerPublicKeyTypes
+utils.deprecated(
+    CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES,
+    __name__,
+    "Use CertificateIssuerPublicKeyTypes instead",
+    utils.DeprecatedIn40,
+    name="CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES",
+)
+# This type removes DHPublicKey. x448/x25519 can be a public key
+# but cannot be used in signing so they are allowed here.
+CertificatePublicKeyTypes = typing.Union[
+    dsa.DSAPublicKey,
+    rsa.RSAPublicKey,
+    ec.EllipticCurvePublicKey,
+    ed25519.Ed25519PublicKey,
+    ed448.Ed448PublicKey,
+    x25519.X25519PublicKey,
+    x448.X448PublicKey,
+]
+CERTIFICATE_PUBLIC_KEY_TYPES = CertificatePublicKeyTypes
+utils.deprecated(
+    CERTIFICATE_PUBLIC_KEY_TYPES,
+    __name__,
+    "Use CertificatePublicKeyTypes instead",
+    utils.DeprecatedIn40,
+    name="CERTIFICATE_PUBLIC_KEY_TYPES",
+)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..826b9567b47bc704902eb959fd21376c8969f695
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py
@@ -0,0 +1,24 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import asn1
+from cryptography.hazmat.primitives import hashes
+
+decode_dss_signature = asn1.decode_dss_signature
+encode_dss_signature = asn1.encode_dss_signature
+
+
+class Prehashed:
+    def __init__(self, algorithm: hashes.HashAlgorithm):
+        if not isinstance(algorithm, hashes.HashAlgorithm):
+            raise TypeError("Expected instance of HashAlgorithm.")
+
+        self._algorithm = algorithm
+        self._digest_size = algorithm.digest_size
+
+    @property
+    def digest_size(self) -> int:
+        return self._digest_size
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py
new file mode 100644
index 0000000000000000000000000000000000000000..0cfa36e346adb05649de4b3e968253d477f8bd3f
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py
@@ -0,0 +1,109 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization
+
+
+class X25519PublicKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def from_public_bytes(cls, data: bytes) -> X25519PublicKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.x25519_supported():
+            raise UnsupportedAlgorithm(
+                "X25519 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
+            )
+
+        return rust_openssl.x25519.from_public_bytes(data)
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        The serialized bytes of the public key.
+        """
+
+    @abc.abstractmethod
+    def public_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the public key.
+        Equivalent to public_bytes(Raw, Raw).
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey)
+
+
+class X25519PrivateKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def generate(cls) -> X25519PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.x25519_supported():
+            raise UnsupportedAlgorithm(
+                "X25519 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
+            )
+        return rust_openssl.x25519.generate_key()
+
+    @classmethod
+    def from_private_bytes(cls, data: bytes) -> X25519PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.x25519_supported():
+            raise UnsupportedAlgorithm(
+                "X25519 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
+            )
+
+        return rust_openssl.x25519.from_private_bytes(data)
+
+    @abc.abstractmethod
+    def public_key(self) -> X25519PublicKey:
+        """
+        Returns the public key associated with this private key
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        The serialized bytes of the private key.
+        """
+
+    @abc.abstractmethod
+    def private_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the private key.
+        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
+        """
+
+    @abc.abstractmethod
+    def exchange(self, peer_public_key: X25519PublicKey) -> bytes:
+        """
+        Performs a key exchange operation using the provided peer's public key.
+        """
+
+
+X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py
new file mode 100644
index 0000000000000000000000000000000000000000..86086ab44855fdcda38adbbdcc593891b5db54cf
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py
@@ -0,0 +1,112 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import _serialization
+
+
+class X448PublicKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def from_public_bytes(cls, data: bytes) -> X448PublicKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.x448_supported():
+            raise UnsupportedAlgorithm(
+                "X448 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
+            )
+
+        return rust_openssl.x448.from_public_bytes(data)
+
+    @abc.abstractmethod
+    def public_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PublicFormat,
+    ) -> bytes:
+        """
+        The serialized bytes of the public key.
+        """
+
+    @abc.abstractmethod
+    def public_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the public key.
+        Equivalent to public_bytes(Raw, Raw).
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+
+if hasattr(rust_openssl, "x448"):
+    X448PublicKey.register(rust_openssl.x448.X448PublicKey)
+
+
+class X448PrivateKey(metaclass=abc.ABCMeta):
+    @classmethod
+    def generate(cls) -> X448PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.x448_supported():
+            raise UnsupportedAlgorithm(
+                "X448 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
+            )
+
+        return rust_openssl.x448.generate_key()
+
+    @classmethod
+    def from_private_bytes(cls, data: bytes) -> X448PrivateKey:
+        from cryptography.hazmat.backends.openssl.backend import backend
+
+        if not backend.x448_supported():
+            raise UnsupportedAlgorithm(
+                "X448 is not supported by this version of OpenSSL.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
+            )
+
+        return rust_openssl.x448.from_private_bytes(data)
+
+    @abc.abstractmethod
+    def public_key(self) -> X448PublicKey:
+        """
+        Returns the public key associated with this private key
+        """
+
+    @abc.abstractmethod
+    def private_bytes(
+        self,
+        encoding: _serialization.Encoding,
+        format: _serialization.PrivateFormat,
+        encryption_algorithm: _serialization.KeySerializationEncryption,
+    ) -> bytes:
+        """
+        The serialized bytes of the private key.
+        """
+
+    @abc.abstractmethod
+    def private_bytes_raw(self) -> bytes:
+        """
+        The raw bytes of the private key.
+        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
+        """
+
+    @abc.abstractmethod
+    def exchange(self, peer_public_key: X448PublicKey) -> bytes:
+        """
+        Performs a key exchange operation using the provided peer's public key.
+        """
+
+
+if hasattr(rust_openssl, "x448"):
+    X448PrivateKey.register(rust_openssl.x448.X448PrivateKey)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..10c15d0f5cb36f19ce23b3a4255e92ef27998c9b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py
@@ -0,0 +1,27 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.primitives._cipheralgorithm import (
+    BlockCipherAlgorithm,
+    CipherAlgorithm,
+)
+from cryptography.hazmat.primitives.ciphers.base import (
+    AEADCipherContext,
+    AEADDecryptionContext,
+    AEADEncryptionContext,
+    Cipher,
+    CipherContext,
+)
+
+__all__ = [
+    "AEADCipherContext",
+    "AEADDecryptionContext",
+    "AEADEncryptionContext",
+    "BlockCipherAlgorithm",
+    "Cipher",
+    "CipherAlgorithm",
+    "CipherContext",
+]
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/aead.py
new file mode 100644
index 0000000000000000000000000000000000000000..c8a582d7844ddf57670ba657b69b5ac1f20af1d1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/aead.py
@@ -0,0 +1,23 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+
+__all__ = [
+    "AESCCM",
+    "AESGCM",
+    "AESGCMSIV",
+    "AESOCB3",
+    "AESSIV",
+    "ChaCha20Poly1305",
+]
+
+AESGCM = rust_openssl.aead.AESGCM
+ChaCha20Poly1305 = rust_openssl.aead.ChaCha20Poly1305
+AESCCM = rust_openssl.aead.AESCCM
+AESSIV = rust_openssl.aead.AESSIV
+AESOCB3 = rust_openssl.aead.AESOCB3
+AESGCMSIV = rust_openssl.aead.AESGCMSIV
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py
new file mode 100644
index 0000000000000000000000000000000000000000..1051ba323506b6008f80a4dbc48075722a612ee8
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -0,0 +1,177 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography import utils
+from cryptography.hazmat.decrepit.ciphers.algorithms import (
+    ARC4 as ARC4,
+)
+from cryptography.hazmat.decrepit.ciphers.algorithms import (
+    CAST5 as CAST5,
+)
+from cryptography.hazmat.decrepit.ciphers.algorithms import (
+    IDEA as IDEA,
+)
+from cryptography.hazmat.decrepit.ciphers.algorithms import (
+    SEED as SEED,
+)
+from cryptography.hazmat.decrepit.ciphers.algorithms import (
+    Blowfish as Blowfish,
+)
+from cryptography.hazmat.decrepit.ciphers.algorithms import (
+    TripleDES as TripleDES,
+)
+from cryptography.hazmat.primitives._cipheralgorithm import _verify_key_size
+from cryptography.hazmat.primitives.ciphers import (
+    BlockCipherAlgorithm,
+    CipherAlgorithm,
+)
+
+
+class AES(BlockCipherAlgorithm):
+    name = "AES"
+    block_size = 128
+    # 512 added to support AES-256-XTS, which uses 512-bit keys
+    key_sizes = frozenset([128, 192, 256, 512])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class AES128(BlockCipherAlgorithm):
+    name = "AES"
+    block_size = 128
+    key_sizes = frozenset([128])
+    key_size = 128
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+
+class AES256(BlockCipherAlgorithm):
+    name = "AES"
+    block_size = 128
+    key_sizes = frozenset([256])
+    key_size = 256
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+
+class Camellia(BlockCipherAlgorithm):
+    name = "camellia"
+    block_size = 128
+    key_sizes = frozenset([128, 192, 256])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+utils.deprecated(
+    ARC4,
+    __name__,
+    "ARC4 has been moved to "
+    "cryptography.hazmat.decrepit.ciphers.algorithms.ARC4 and "
+    "will be removed from this module in 48.0.0.",
+    utils.DeprecatedIn43,
+    name="ARC4",
+)
+
+
+utils.deprecated(
+    TripleDES,
+    __name__,
+    "TripleDES has been moved to "
+    "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and "
+    "will be removed from this module in 48.0.0.",
+    utils.DeprecatedIn43,
+    name="TripleDES",
+)
+
+utils.deprecated(
+    Blowfish,
+    __name__,
+    "Blowfish has been moved to "
+    "cryptography.hazmat.decrepit.ciphers.algorithms.Blowfish and "
+    "will be removed from this module in 45.0.0.",
+    utils.DeprecatedIn37,
+    name="Blowfish",
+)
+
+
+utils.deprecated(
+    CAST5,
+    __name__,
+    "CAST5 has been moved to "
+    "cryptography.hazmat.decrepit.ciphers.algorithms.CAST5 and "
+    "will be removed from this module in 45.0.0.",
+    utils.DeprecatedIn37,
+    name="CAST5",
+)
+
+
+utils.deprecated(
+    IDEA,
+    __name__,
+    "IDEA has been moved to "
+    "cryptography.hazmat.decrepit.ciphers.algorithms.IDEA and "
+    "will be removed from this module in 45.0.0.",
+    utils.DeprecatedIn37,
+    name="IDEA",
+)
+
+
+utils.deprecated(
+    SEED,
+    __name__,
+    "SEED has been moved to "
+    "cryptography.hazmat.decrepit.ciphers.algorithms.SEED and "
+    "will be removed from this module in 45.0.0.",
+    utils.DeprecatedIn37,
+    name="SEED",
+)
+
+
+class ChaCha20(CipherAlgorithm):
+    name = "ChaCha20"
+    key_sizes = frozenset([256])
+
+    def __init__(self, key: bytes, nonce: bytes):
+        self.key = _verify_key_size(self, key)
+        utils._check_byteslike("nonce", nonce)
+
+        if len(nonce) != 16:
+            raise ValueError("nonce must be 128-bits (16 bytes)")
+
+        self._nonce = nonce
+
+    @property
+    def nonce(self) -> bytes:
+        return self._nonce
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
+
+
+class SM4(BlockCipherAlgorithm):
+    name = "SM4"
+    block_size = 128
+    key_sizes = frozenset([128])
+
+    def __init__(self, key: bytes):
+        self.key = _verify_key_size(self, key)
+
+    @property
+    def key_size(self) -> int:
+        return len(self.key) * 8
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..ebfa8052c8da765c53b265bbe6fca6383b7feb26
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/base.py
@@ -0,0 +1,145 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import typing
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm
+from cryptography.hazmat.primitives.ciphers import modes
+
+
+class CipherContext(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def update(self, data: bytes) -> bytes:
+        """
+        Processes the provided bytes through the cipher and returns the results
+        as bytes.
+        """
+
+    @abc.abstractmethod
+    def update_into(self, data: bytes, buf: bytes) -> int:
+        """
+        Processes the provided bytes and writes the resulting data into the
+        provided buffer. Returns the number of bytes written.
+        """
+
+    @abc.abstractmethod
+    def finalize(self) -> bytes:
+        """
+        Returns the results of processing the final block as bytes.
+        """
+
+    @abc.abstractmethod
+    def reset_nonce(self, nonce: bytes) -> None:
+        """
+        Resets the nonce for the cipher context to the provided value.
+        Raises an exception if it does not support reset or if the
+        provided nonce does not have a valid length.
+        """
+
+
+class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def authenticate_additional_data(self, data: bytes) -> None:
+        """
+        Authenticates the provided bytes.
+        """
+
+
+class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def finalize_with_tag(self, tag: bytes) -> bytes:
+        """
+        Returns the results of processing the final block as bytes and allows
+        delayed passing of the authentication tag.
+        """
+
+
+class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def tag(self) -> bytes:
+        """
+        Returns tag bytes. This is only available after encryption is
+        finalized.
+        """
+
+
+Mode = typing.TypeVar(
+    "Mode", bound=typing.Optional[modes.Mode], covariant=True
+)
+
+
+class Cipher(typing.Generic[Mode]):
+    def __init__(
+        self,
+        algorithm: CipherAlgorithm,
+        mode: Mode,
+        backend: typing.Any = None,
+    ) -> None:
+        if not isinstance(algorithm, CipherAlgorithm):
+            raise TypeError("Expected interface of CipherAlgorithm.")
+
+        if mode is not None:
+            # mypy needs this assert to narrow the type from our generic
+            # type. Maybe it won't some time in the future.
+            assert isinstance(mode, modes.Mode)
+            mode.validate_for_algorithm(algorithm)
+
+        self.algorithm = algorithm
+        self.mode = mode
+
+    @typing.overload
+    def encryptor(
+        self: Cipher[modes.ModeWithAuthenticationTag],
+    ) -> AEADEncryptionContext: ...
+
+    @typing.overload
+    def encryptor(
+        self: _CIPHER_TYPE,
+    ) -> CipherContext: ...
+
+    def encryptor(self):
+        if isinstance(self.mode, modes.ModeWithAuthenticationTag):
+            if self.mode.tag is not None:
+                raise ValueError(
+                    "Authentication tag must be None when encrypting."
+                )
+
+        return rust_openssl.ciphers.create_encryption_ctx(
+            self.algorithm, self.mode
+        )
+
+    @typing.overload
+    def decryptor(
+        self: Cipher[modes.ModeWithAuthenticationTag],
+    ) -> AEADDecryptionContext: ...
+
+    @typing.overload
+    def decryptor(
+        self: _CIPHER_TYPE,
+    ) -> CipherContext: ...
+
+    def decryptor(self):
+        return rust_openssl.ciphers.create_decryption_ctx(
+            self.algorithm, self.mode
+        )
+
+
+_CIPHER_TYPE = Cipher[
+    typing.Union[
+        modes.ModeWithNonce,
+        modes.ModeWithTweak,
+        None,
+        modes.ECB,
+        modes.ModeWithInitializationVector,
+    ]
+]
+
+CipherContext.register(rust_openssl.ciphers.CipherContext)
+AEADEncryptionContext.register(rust_openssl.ciphers.AEADEncryptionContext)
+AEADDecryptionContext.register(rust_openssl.ciphers.AEADDecryptionContext)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/modes.py
new file mode 100644
index 0000000000000000000000000000000000000000..1dd2cc1e80c397f1b2b53e373d98f58a616d9305
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/modes.py
@@ -0,0 +1,268 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography import utils
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.primitives._cipheralgorithm import (
+    BlockCipherAlgorithm,
+    CipherAlgorithm,
+)
+from cryptography.hazmat.primitives.ciphers import algorithms
+
+
+class Mode(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def name(self) -> str:
+        """
+        A string naming this mode (e.g. "ECB", "CBC").
+        """
+
+    @abc.abstractmethod
+    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
+        """
+        Checks that all the necessary invariants of this (mode, algorithm)
+        combination are met.
+        """
+
+
+class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def initialization_vector(self) -> bytes:
+        """
+        The value of the initialization vector for this mode as bytes.
+        """
+
+
+class ModeWithTweak(Mode, metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def tweak(self) -> bytes:
+        """
+        The value of the tweak for this mode as bytes.
+        """
+
+
+class ModeWithNonce(Mode, metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def nonce(self) -> bytes:
+        """
+        The value of the nonce for this mode as bytes.
+        """
+
+
+class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def tag(self) -> bytes | None:
+        """
+        The value of the tag supplied to the constructor of this mode.
+        """
+
+
+def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None:
+    if algorithm.key_size > 256 and algorithm.name == "AES":
+        raise ValueError(
+            "Only 128, 192, and 256 bit keys are allowed for this AES mode"
+        )
+
+
+def _check_iv_length(
+    self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm
+) -> None:
+    iv_len = len(self.initialization_vector)
+    if iv_len * 8 != algorithm.block_size:
+        raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.")
+
+
+def _check_nonce_length(
+    nonce: bytes, name: str, algorithm: CipherAlgorithm
+) -> None:
+    if not isinstance(algorithm, BlockCipherAlgorithm):
+        raise UnsupportedAlgorithm(
+            f"{name} requires a block cipher algorithm",
+            _Reasons.UNSUPPORTED_CIPHER,
+        )
+    if len(nonce) * 8 != algorithm.block_size:
+        raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.")
+
+
+def _check_iv_and_key_length(
+    self: ModeWithInitializationVector, algorithm: CipherAlgorithm
+) -> None:
+    if not isinstance(algorithm, BlockCipherAlgorithm):
+        raise UnsupportedAlgorithm(
+            f"{self} requires a block cipher algorithm",
+            _Reasons.UNSUPPORTED_CIPHER,
+        )
+    _check_aes_key_length(self, algorithm)
+    _check_iv_length(self, algorithm)
+
+
+class CBC(ModeWithInitializationVector):
+    name = "CBC"
+
+    def __init__(self, initialization_vector: bytes):
+        utils._check_byteslike("initialization_vector", initialization_vector)
+        self._initialization_vector = initialization_vector
+
+    @property
+    def initialization_vector(self) -> bytes:
+        return self._initialization_vector
+
+    validate_for_algorithm = _check_iv_and_key_length
+
+
+class XTS(ModeWithTweak):
+    name = "XTS"
+
+    def __init__(self, tweak: bytes):
+        utils._check_byteslike("tweak", tweak)
+
+        if len(tweak) != 16:
+            raise ValueError("tweak must be 128-bits (16 bytes)")
+
+        self._tweak = tweak
+
+    @property
+    def tweak(self) -> bytes:
+        return self._tweak
+
+    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
+        if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)):
+            raise TypeError(
+                "The AES128 and AES256 classes do not support XTS, please use "
+                "the standard AES class instead."
+            )
+
+        if algorithm.key_size not in (256, 512):
+            raise ValueError(
+                "The XTS specification requires a 256-bit key for AES-128-XTS"
+                " and 512-bit key for AES-256-XTS"
+            )
+
+
+class ECB(Mode):
+    name = "ECB"
+
+    validate_for_algorithm = _check_aes_key_length
+
+
+class OFB(ModeWithInitializationVector):
+    name = "OFB"
+
+    def __init__(self, initialization_vector: bytes):
+        utils._check_byteslike("initialization_vector", initialization_vector)
+        self._initialization_vector = initialization_vector
+
+    @property
+    def initialization_vector(self) -> bytes:
+        return self._initialization_vector
+
+    validate_for_algorithm = _check_iv_and_key_length
+
+
+class CFB(ModeWithInitializationVector):
+    name = "CFB"
+
+    def __init__(self, initialization_vector: bytes):
+        utils._check_byteslike("initialization_vector", initialization_vector)
+        self._initialization_vector = initialization_vector
+
+    @property
+    def initialization_vector(self) -> bytes:
+        return self._initialization_vector
+
+    validate_for_algorithm = _check_iv_and_key_length
+
+
+class CFB8(ModeWithInitializationVector):
+    name = "CFB8"
+
+    def __init__(self, initialization_vector: bytes):
+        utils._check_byteslike("initialization_vector", initialization_vector)
+        self._initialization_vector = initialization_vector
+
+    @property
+    def initialization_vector(self) -> bytes:
+        return self._initialization_vector
+
+    validate_for_algorithm = _check_iv_and_key_length
+
+
+class CTR(ModeWithNonce):
+    name = "CTR"
+
+    def __init__(self, nonce: bytes):
+        utils._check_byteslike("nonce", nonce)
+        self._nonce = nonce
+
+    @property
+    def nonce(self) -> bytes:
+        return self._nonce
+
+    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
+        _check_aes_key_length(self, algorithm)
+        _check_nonce_length(self.nonce, self.name, algorithm)
+
+
+class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag):
+    name = "GCM"
+    _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8
+    _MAX_AAD_BYTES = (2**64) // 8
+
+    def __init__(
+        self,
+        initialization_vector: bytes,
+        tag: bytes | None = None,
+        min_tag_length: int = 16,
+    ):
+        # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive
+        # This is a sane limit anyway so we'll enforce it here.
+        utils._check_byteslike("initialization_vector", initialization_vector)
+        if len(initialization_vector) < 8 or len(initialization_vector) > 128:
+            raise ValueError(
+                "initialization_vector must be between 8 and 128 bytes (64 "
+                "and 1024 bits)."
+            )
+        self._initialization_vector = initialization_vector
+        if tag is not None:
+            utils._check_bytes("tag", tag)
+            if min_tag_length < 4:
+                raise ValueError("min_tag_length must be >= 4")
+            if len(tag) < min_tag_length:
+                raise ValueError(
+                    f"Authentication tag must be {min_tag_length} bytes or "
+                    "longer."
+                )
+        self._tag = tag
+        self._min_tag_length = min_tag_length
+
+    @property
+    def tag(self) -> bytes | None:
+        return self._tag
+
+    @property
+    def initialization_vector(self) -> bytes:
+        return self._initialization_vector
+
+    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
+        _check_aes_key_length(self, algorithm)
+        if not isinstance(algorithm, BlockCipherAlgorithm):
+            raise UnsupportedAlgorithm(
+                "GCM requires a block cipher algorithm",
+                _Reasons.UNSUPPORTED_CIPHER,
+            )
+        block_size_bytes = algorithm.block_size // 8
+        if self._tag is not None and len(self._tag) > block_size_bytes:
+            raise ValueError(
+                f"Authentication tag cannot be more than {block_size_bytes} "
+                "bytes."
+            )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/cmac.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/cmac.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c67ce2206e4c36c7ab8c4a49ba9d87f07529598
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/cmac.py
@@ -0,0 +1,10 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+
+__all__ = ["CMAC"]
+CMAC = rust_openssl.cmac.CMAC
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/constant_time.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/constant_time.py
new file mode 100644
index 0000000000000000000000000000000000000000..3975c7147eb92b56685423aa1c5810adcf253a23
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/constant_time.py
@@ -0,0 +1,14 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import hmac
+
+
+def bytes_eq(a: bytes, b: bytes) -> bool:
+    if not isinstance(a, bytes) or not isinstance(b, bytes):
+        raise TypeError("a and b must be bytes.")
+
+    return hmac.compare_digest(a, b)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/hashes.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/hashes.py
new file mode 100644
index 0000000000000000000000000000000000000000..b819e399287e31260ff8ed6a38243e5fcd0a6575
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/hashes.py
@@ -0,0 +1,242 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+
+__all__ = [
+    "MD5",
+    "SHA1",
+    "SHA3_224",
+    "SHA3_256",
+    "SHA3_384",
+    "SHA3_512",
+    "SHA224",
+    "SHA256",
+    "SHA384",
+    "SHA512",
+    "SHA512_224",
+    "SHA512_256",
+    "SHAKE128",
+    "SHAKE256",
+    "SM3",
+    "BLAKE2b",
+    "BLAKE2s",
+    "ExtendableOutputFunction",
+    "Hash",
+    "HashAlgorithm",
+    "HashContext",
+]
+
+
+class HashAlgorithm(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def name(self) -> str:
+        """
+        A string naming this algorithm (e.g. "sha256", "md5").
+        """
+
+    @property
+    @abc.abstractmethod
+    def digest_size(self) -> int:
+        """
+        The size of the resulting digest in bytes.
+        """
+
+    @property
+    @abc.abstractmethod
+    def block_size(self) -> int | None:
+        """
+        The internal block size of the hash function, or None if the hash
+        function does not use blocks internally (e.g. SHA3).
+        """
+
+
+class HashContext(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def algorithm(self) -> HashAlgorithm:
+        """
+        A HashAlgorithm that will be used by this context.
+        """
+
+    @abc.abstractmethod
+    def update(self, data: bytes) -> None:
+        """
+        Processes the provided bytes through the hash.
+        """
+
+    @abc.abstractmethod
+    def finalize(self) -> bytes:
+        """
+        Finalizes the hash context and returns the hash digest as bytes.
+        """
+
+    @abc.abstractmethod
+    def copy(self) -> HashContext:
+        """
+        Return a HashContext that is a copy of the current context.
+        """
+
+
+Hash = rust_openssl.hashes.Hash
+HashContext.register(Hash)
+
+
+class ExtendableOutputFunction(metaclass=abc.ABCMeta):
+    """
+    An interface for extendable output functions.
+    """
+
+
+class SHA1(HashAlgorithm):
+    name = "sha1"
+    digest_size = 20
+    block_size = 64
+
+
+class SHA512_224(HashAlgorithm):  # noqa: N801
+    name = "sha512-224"
+    digest_size = 28
+    block_size = 128
+
+
+class SHA512_256(HashAlgorithm):  # noqa: N801
+    name = "sha512-256"
+    digest_size = 32
+    block_size = 128
+
+
+class SHA224(HashAlgorithm):
+    name = "sha224"
+    digest_size = 28
+    block_size = 64
+
+
+class SHA256(HashAlgorithm):
+    name = "sha256"
+    digest_size = 32
+    block_size = 64
+
+
+class SHA384(HashAlgorithm):
+    name = "sha384"
+    digest_size = 48
+    block_size = 128
+
+
+class SHA512(HashAlgorithm):
+    name = "sha512"
+    digest_size = 64
+    block_size = 128
+
+
+class SHA3_224(HashAlgorithm):  # noqa: N801
+    name = "sha3-224"
+    digest_size = 28
+    block_size = None
+
+
+class SHA3_256(HashAlgorithm):  # noqa: N801
+    name = "sha3-256"
+    digest_size = 32
+    block_size = None
+
+
+class SHA3_384(HashAlgorithm):  # noqa: N801
+    name = "sha3-384"
+    digest_size = 48
+    block_size = None
+
+
+class SHA3_512(HashAlgorithm):  # noqa: N801
+    name = "sha3-512"
+    digest_size = 64
+    block_size = None
+
+
+class SHAKE128(HashAlgorithm, ExtendableOutputFunction):
+    name = "shake128"
+    block_size = None
+
+    def __init__(self, digest_size: int):
+        if not isinstance(digest_size, int):
+            raise TypeError("digest_size must be an integer")
+
+        if digest_size < 1:
+            raise ValueError("digest_size must be a positive integer")
+
+        self._digest_size = digest_size
+
+    @property
+    def digest_size(self) -> int:
+        return self._digest_size
+
+
+class SHAKE256(HashAlgorithm, ExtendableOutputFunction):
+    name = "shake256"
+    block_size = None
+
+    def __init__(self, digest_size: int):
+        if not isinstance(digest_size, int):
+            raise TypeError("digest_size must be an integer")
+
+        if digest_size < 1:
+            raise ValueError("digest_size must be a positive integer")
+
+        self._digest_size = digest_size
+
+    @property
+    def digest_size(self) -> int:
+        return self._digest_size
+
+
+class MD5(HashAlgorithm):
+    name = "md5"
+    digest_size = 16
+    block_size = 64
+
+
+class BLAKE2b(HashAlgorithm):
+    name = "blake2b"
+    _max_digest_size = 64
+    _min_digest_size = 1
+    block_size = 128
+
+    def __init__(self, digest_size: int):
+        if digest_size != 64:
+            raise ValueError("Digest size must be 64")
+
+        self._digest_size = digest_size
+
+    @property
+    def digest_size(self) -> int:
+        return self._digest_size
+
+
+class BLAKE2s(HashAlgorithm):
+    name = "blake2s"
+    block_size = 64
+    _max_digest_size = 32
+    _min_digest_size = 1
+
+    def __init__(self, digest_size: int):
+        if digest_size != 32:
+            raise ValueError("Digest size must be 32")
+
+        self._digest_size = digest_size
+
+    @property
+    def digest_size(self) -> int:
+        return self._digest_size
+
+
+class SM3(HashAlgorithm):
+    name = "sm3"
+    digest_size = 32
+    block_size = 64
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/hmac.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/hmac.py
new file mode 100644
index 0000000000000000000000000000000000000000..a9442d59ab474ac680ff7253f2ca0e67d0e93c1a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/hmac.py
@@ -0,0 +1,13 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import hashes
+
+__all__ = ["HMAC"]
+
+HMAC = rust_openssl.hmac.HMAC
+hashes.HashContext.register(HMAC)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..79bb459f01ec288d67b4f7c20fd5af436fcab4a5
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/__init__.py
@@ -0,0 +1,23 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+
+
+class KeyDerivationFunction(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def derive(self, key_material: bytes) -> bytes:
+        """
+        Deterministically generates and returns a new key based on the existing
+        key material.
+        """
+
+    @abc.abstractmethod
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        """
+        Checks whether the key generated by the key material matches the
+        expected derived key. Raises an exception if they do not match.
+        """
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..96d9d4c0df5ea99edfa5a5eabf599b7bacc28cfd
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py
@@ -0,0 +1,124 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import AlreadyFinalized, InvalidKey
+from cryptography.hazmat.primitives import constant_time, hashes, hmac
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+
+def _int_to_u32be(n: int) -> bytes:
+    return n.to_bytes(length=4, byteorder="big")
+
+
+def _common_args_checks(
+    algorithm: hashes.HashAlgorithm,
+    length: int,
+    otherinfo: bytes | None,
+) -> None:
+    max_length = algorithm.digest_size * (2**32 - 1)
+    if length > max_length:
+        raise ValueError(f"Cannot derive keys larger than {max_length} bits.")
+    if otherinfo is not None:
+        utils._check_bytes("otherinfo", otherinfo)
+
+
+def _concatkdf_derive(
+    key_material: bytes,
+    length: int,
+    auxfn: typing.Callable[[], hashes.HashContext],
+    otherinfo: bytes,
+) -> bytes:
+    utils._check_byteslike("key_material", key_material)
+    output = [b""]
+    outlen = 0
+    counter = 1
+
+    while length > outlen:
+        h = auxfn()
+        h.update(_int_to_u32be(counter))
+        h.update(key_material)
+        h.update(otherinfo)
+        output.append(h.finalize())
+        outlen += len(output[-1])
+        counter += 1
+
+    return b"".join(output)[:length]
+
+
+class ConcatKDFHash(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        length: int,
+        otherinfo: bytes | None,
+        backend: typing.Any = None,
+    ):
+        _common_args_checks(algorithm, length, otherinfo)
+        self._algorithm = algorithm
+        self._length = length
+        self._otherinfo: bytes = otherinfo if otherinfo is not None else b""
+
+        self._used = False
+
+    def _hash(self) -> hashes.Hash:
+        return hashes.Hash(self._algorithm)
+
+    def derive(self, key_material: bytes) -> bytes:
+        if self._used:
+            raise AlreadyFinalized
+        self._used = True
+        return _concatkdf_derive(
+            key_material, self._length, self._hash, self._otherinfo
+        )
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
+
+
+class ConcatKDFHMAC(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        length: int,
+        salt: bytes | None,
+        otherinfo: bytes | None,
+        backend: typing.Any = None,
+    ):
+        _common_args_checks(algorithm, length, otherinfo)
+        self._algorithm = algorithm
+        self._length = length
+        self._otherinfo: bytes = otherinfo if otherinfo is not None else b""
+
+        if algorithm.block_size is None:
+            raise TypeError(f"{algorithm.name} is unsupported for ConcatKDF")
+
+        if salt is None:
+            salt = b"\x00" * algorithm.block_size
+        else:
+            utils._check_bytes("salt", salt)
+
+        self._salt = salt
+
+        self._used = False
+
+    def _hmac(self) -> hmac.HMAC:
+        return hmac.HMAC(self._salt, self._algorithm)
+
+    def derive(self, key_material: bytes) -> bytes:
+        if self._used:
+            raise AlreadyFinalized
+        self._used = True
+        return _concatkdf_derive(
+            key_material, self._length, self._hmac, self._otherinfo
+        )
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee562d2f4433393d05e1ab9c316e262ee8d058e1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -0,0 +1,101 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import AlreadyFinalized, InvalidKey
+from cryptography.hazmat.primitives import constant_time, hashes, hmac
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+
+class HKDF(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        length: int,
+        salt: bytes | None,
+        info: bytes | None,
+        backend: typing.Any = None,
+    ):
+        self._algorithm = algorithm
+
+        if salt is None:
+            salt = b"\x00" * self._algorithm.digest_size
+        else:
+            utils._check_bytes("salt", salt)
+
+        self._salt = salt
+
+        self._hkdf_expand = HKDFExpand(self._algorithm, length, info)
+
+    def _extract(self, key_material: bytes) -> bytes:
+        h = hmac.HMAC(self._salt, self._algorithm)
+        h.update(key_material)
+        return h.finalize()
+
+    def derive(self, key_material: bytes) -> bytes:
+        utils._check_byteslike("key_material", key_material)
+        return self._hkdf_expand.derive(self._extract(key_material))
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
+
+
+class HKDFExpand(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        length: int,
+        info: bytes | None,
+        backend: typing.Any = None,
+    ):
+        self._algorithm = algorithm
+
+        max_length = 255 * algorithm.digest_size
+
+        if length > max_length:
+            raise ValueError(
+                f"Cannot derive keys larger than {max_length} octets."
+            )
+
+        self._length = length
+
+        if info is None:
+            info = b""
+        else:
+            utils._check_bytes("info", info)
+
+        self._info = info
+
+        self._used = False
+
+    def _expand(self, key_material: bytes) -> bytes:
+        output = [b""]
+        counter = 1
+
+        while self._algorithm.digest_size * (len(output) - 1) < self._length:
+            h = hmac.HMAC(key_material, self._algorithm)
+            h.update(output[-1])
+            h.update(self._info)
+            h.update(bytes([counter]))
+            output.append(h.finalize())
+            counter += 1
+
+        return b"".join(output)[: self._length]
+
+    def derive(self, key_material: bytes) -> bytes:
+        utils._check_byteslike("key_material", key_material)
+        if self._used:
+            raise AlreadyFinalized
+
+        self._used = True
+        return self._expand(key_material)
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..802b484c72ae92d6ddb37ec1a4d7d4b5694db4bf
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py
@@ -0,0 +1,302 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import (
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
+)
+from cryptography.hazmat.primitives import (
+    ciphers,
+    cmac,
+    constant_time,
+    hashes,
+    hmac,
+)
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+
+class Mode(utils.Enum):
+    CounterMode = "ctr"
+
+
+class CounterLocation(utils.Enum):
+    BeforeFixed = "before_fixed"
+    AfterFixed = "after_fixed"
+    MiddleFixed = "middle_fixed"
+
+
+class _KBKDFDeriver:
+    def __init__(
+        self,
+        prf: typing.Callable,
+        mode: Mode,
+        length: int,
+        rlen: int,
+        llen: int | None,
+        location: CounterLocation,
+        break_location: int | None,
+        label: bytes | None,
+        context: bytes | None,
+        fixed: bytes | None,
+    ):
+        assert callable(prf)
+
+        if not isinstance(mode, Mode):
+            raise TypeError("mode must be of type Mode")
+
+        if not isinstance(location, CounterLocation):
+            raise TypeError("location must be of type CounterLocation")
+
+        if break_location is None and location is CounterLocation.MiddleFixed:
+            raise ValueError("Please specify a break_location")
+
+        if (
+            break_location is not None
+            and location != CounterLocation.MiddleFixed
+        ):
+            raise ValueError(
+                "break_location is ignored when location is not"
+                " CounterLocation.MiddleFixed"
+            )
+
+        if break_location is not None and not isinstance(break_location, int):
+            raise TypeError("break_location must be an integer")
+
+        if break_location is not None and break_location < 0:
+            raise ValueError("break_location must be a positive integer")
+
+        if (label or context) and fixed:
+            raise ValueError(
+                "When supplying fixed data, label and context are ignored."
+            )
+
+        if rlen is None or not self._valid_byte_length(rlen):
+            raise ValueError("rlen must be between 1 and 4")
+
+        if llen is None and fixed is None:
+            raise ValueError("Please specify an llen")
+
+        if llen is not None and not isinstance(llen, int):
+            raise TypeError("llen must be an integer")
+
+        if llen == 0:
+            raise ValueError("llen must be non-zero")
+
+        if label is None:
+            label = b""
+
+        if context is None:
+            context = b""
+
+        utils._check_bytes("label", label)
+        utils._check_bytes("context", context)
+        self._prf = prf
+        self._mode = mode
+        self._length = length
+        self._rlen = rlen
+        self._llen = llen
+        self._location = location
+        self._break_location = break_location
+        self._label = label
+        self._context = context
+        self._used = False
+        self._fixed_data = fixed
+
+    @staticmethod
+    def _valid_byte_length(value: int) -> bool:
+        if not isinstance(value, int):
+            raise TypeError("value must be of type int")
+
+        value_bin = utils.int_to_bytes(1, value)
+        if not 1 <= len(value_bin) <= 4:
+            return False
+        return True
+
+    def derive(self, key_material: bytes, prf_output_size: int) -> bytes:
+        if self._used:
+            raise AlreadyFinalized
+
+        utils._check_byteslike("key_material", key_material)
+        self._used = True
+
+        # inverse floor division (equivalent to ceiling)
+        rounds = -(-self._length // prf_output_size)
+
+        output = [b""]
+
+        # For counter mode, the number of iterations shall not be
+        # larger than 2^r-1, where r <= 32 is the binary length of the counter
+        # This ensures that the counter values used as an input to the
+        # PRF will not repeat during a particular call to the KDF function.
+        r_bin = utils.int_to_bytes(1, self._rlen)
+        if rounds > pow(2, len(r_bin) * 8) - 1:
+            raise ValueError("There are too many iterations.")
+
+        fixed = self._generate_fixed_input()
+
+        if self._location == CounterLocation.BeforeFixed:
+            data_before_ctr = b""
+            data_after_ctr = fixed
+        elif self._location == CounterLocation.AfterFixed:
+            data_before_ctr = fixed
+            data_after_ctr = b""
+        else:
+            if isinstance(
+                self._break_location, int
+            ) and self._break_location > len(fixed):
+                raise ValueError("break_location offset > len(fixed)")
+            data_before_ctr = fixed[: self._break_location]
+            data_after_ctr = fixed[self._break_location :]
+
+        for i in range(1, rounds + 1):
+            h = self._prf(key_material)
+
+            counter = utils.int_to_bytes(i, self._rlen)
+            input_data = data_before_ctr + counter + data_after_ctr
+
+            h.update(input_data)
+
+            output.append(h.finalize())
+
+        return b"".join(output)[: self._length]
+
+    def _generate_fixed_input(self) -> bytes:
+        if self._fixed_data and isinstance(self._fixed_data, bytes):
+            return self._fixed_data
+
+        l_val = utils.int_to_bytes(self._length * 8, self._llen)
+
+        return b"".join([self._label, b"\x00", self._context, l_val])
+
+
+class KBKDFHMAC(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        mode: Mode,
+        length: int,
+        rlen: int,
+        llen: int | None,
+        location: CounterLocation,
+        label: bytes | None,
+        context: bytes | None,
+        fixed: bytes | None,
+        backend: typing.Any = None,
+        *,
+        break_location: int | None = None,
+    ):
+        if not isinstance(algorithm, hashes.HashAlgorithm):
+            raise UnsupportedAlgorithm(
+                "Algorithm supplied is not a supported hash algorithm.",
+                _Reasons.UNSUPPORTED_HASH,
+            )
+
+        from cryptography.hazmat.backends.openssl.backend import (
+            backend as ossl,
+        )
+
+        if not ossl.hmac_supported(algorithm):
+            raise UnsupportedAlgorithm(
+                "Algorithm supplied is not a supported hmac algorithm.",
+                _Reasons.UNSUPPORTED_HASH,
+            )
+
+        self._algorithm = algorithm
+
+        self._deriver = _KBKDFDeriver(
+            self._prf,
+            mode,
+            length,
+            rlen,
+            llen,
+            location,
+            break_location,
+            label,
+            context,
+            fixed,
+        )
+
+    def _prf(self, key_material: bytes) -> hmac.HMAC:
+        return hmac.HMAC(key_material, self._algorithm)
+
+    def derive(self, key_material: bytes) -> bytes:
+        return self._deriver.derive(key_material, self._algorithm.digest_size)
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
+
+
+class KBKDFCMAC(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm,
+        mode: Mode,
+        length: int,
+        rlen: int,
+        llen: int | None,
+        location: CounterLocation,
+        label: bytes | None,
+        context: bytes | None,
+        fixed: bytes | None,
+        backend: typing.Any = None,
+        *,
+        break_location: int | None = None,
+    ):
+        if not issubclass(
+            algorithm, ciphers.BlockCipherAlgorithm
+        ) or not issubclass(algorithm, ciphers.CipherAlgorithm):
+            raise UnsupportedAlgorithm(
+                "Algorithm supplied is not a supported cipher algorithm.",
+                _Reasons.UNSUPPORTED_CIPHER,
+            )
+
+        self._algorithm = algorithm
+        self._cipher: ciphers.BlockCipherAlgorithm | None = None
+
+        self._deriver = _KBKDFDeriver(
+            self._prf,
+            mode,
+            length,
+            rlen,
+            llen,
+            location,
+            break_location,
+            label,
+            context,
+            fixed,
+        )
+
+    def _prf(self, _: bytes) -> cmac.CMAC:
+        assert self._cipher is not None
+
+        return cmac.CMAC(self._cipher)
+
+    def derive(self, key_material: bytes) -> bytes:
+        self._cipher = self._algorithm(key_material)
+
+        assert self._cipher is not None
+
+        from cryptography.hazmat.backends.openssl.backend import (
+            backend as ossl,
+        )
+
+        if not ossl.cmac_algorithm_supported(self._cipher):
+            raise UnsupportedAlgorithm(
+                "Algorithm supplied is not a supported cipher algorithm.",
+                _Reasons.UNSUPPORTED_CIPHER,
+            )
+
+        return self._deriver.derive(key_material, self._cipher.block_size // 8)
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py
new file mode 100644
index 0000000000000000000000000000000000000000..82689ebca4aec247bd02b7faa5c7a97e6dca291b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py
@@ -0,0 +1,62 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import (
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
+)
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import constant_time, hashes
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+
+class PBKDF2HMAC(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        length: int,
+        salt: bytes,
+        iterations: int,
+        backend: typing.Any = None,
+    ):
+        from cryptography.hazmat.backends.openssl.backend import (
+            backend as ossl,
+        )
+
+        if not ossl.pbkdf2_hmac_supported(algorithm):
+            raise UnsupportedAlgorithm(
+                f"{algorithm.name} is not supported for PBKDF2.",
+                _Reasons.UNSUPPORTED_HASH,
+            )
+        self._used = False
+        self._algorithm = algorithm
+        self._length = length
+        utils._check_bytes("salt", salt)
+        self._salt = salt
+        self._iterations = iterations
+
+    def derive(self, key_material: bytes) -> bytes:
+        if self._used:
+            raise AlreadyFinalized("PBKDF2 instances can only be used once.")
+        self._used = True
+
+        return rust_openssl.kdf.derive_pbkdf2_hmac(
+            key_material,
+            self._algorithm,
+            self._salt,
+            self._iterations,
+            self._length,
+        )
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        derived_key = self.derive(key_material)
+        if not constant_time.bytes_eq(derived_key, expected_key):
+            raise InvalidKey("Keys do not match.")
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py
new file mode 100644
index 0000000000000000000000000000000000000000..05a4f675b6ababd6701cd96850fdf89a28f9d2fd
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py
@@ -0,0 +1,80 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import sys
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import (
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+)
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+from cryptography.hazmat.primitives import constant_time
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+# This is used by the scrypt tests to skip tests that require more memory
+# than the MEM_LIMIT
+_MEM_LIMIT = sys.maxsize // 2
+
+
+class Scrypt(KeyDerivationFunction):
+    def __init__(
+        self,
+        salt: bytes,
+        length: int,
+        n: int,
+        r: int,
+        p: int,
+        backend: typing.Any = None,
+    ):
+        from cryptography.hazmat.backends.openssl.backend import (
+            backend as ossl,
+        )
+
+        if not ossl.scrypt_supported():
+            raise UnsupportedAlgorithm(
+                "This version of OpenSSL does not support scrypt"
+            )
+        self._length = length
+        utils._check_bytes("salt", salt)
+        if n < 2 or (n & (n - 1)) != 0:
+            raise ValueError("n must be greater than 1 and be a power of 2.")
+
+        if r < 1:
+            raise ValueError("r must be greater than or equal to 1.")
+
+        if p < 1:
+            raise ValueError("p must be greater than or equal to 1.")
+
+        self._used = False
+        self._salt = salt
+        self._n = n
+        self._r = r
+        self._p = p
+
+    def derive(self, key_material: bytes) -> bytes:
+        if self._used:
+            raise AlreadyFinalized("Scrypt instances can only be used once.")
+        self._used = True
+
+        utils._check_byteslike("key_material", key_material)
+
+        return rust_openssl.kdf.derive_scrypt(
+            key_material,
+            self._salt,
+            self._n,
+            self._r,
+            self._p,
+            _MEM_LIMIT,
+            self._length,
+        )
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        derived_key = self.derive(key_material)
+        if not constant_time.bytes_eq(derived_key, expected_key):
+            raise InvalidKey("Keys do not match.")
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e38366a996f7fd8c539030cc4c56972f92cfc0b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py
@@ -0,0 +1,61 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import AlreadyFinalized, InvalidKey
+from cryptography.hazmat.primitives import constant_time, hashes
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+
+def _int_to_u32be(n: int) -> bytes:
+    return n.to_bytes(length=4, byteorder="big")
+
+
+class X963KDF(KeyDerivationFunction):
+    def __init__(
+        self,
+        algorithm: hashes.HashAlgorithm,
+        length: int,
+        sharedinfo: bytes | None,
+        backend: typing.Any = None,
+    ):
+        max_len = algorithm.digest_size * (2**32 - 1)
+        if length > max_len:
+            raise ValueError(f"Cannot derive keys larger than {max_len} bits.")
+        if sharedinfo is not None:
+            utils._check_bytes("sharedinfo", sharedinfo)
+
+        self._algorithm = algorithm
+        self._length = length
+        self._sharedinfo = sharedinfo
+        self._used = False
+
+    def derive(self, key_material: bytes) -> bytes:
+        if self._used:
+            raise AlreadyFinalized
+        self._used = True
+        utils._check_byteslike("key_material", key_material)
+        output = [b""]
+        outlen = 0
+        counter = 1
+
+        while self._length > outlen:
+            h = hashes.Hash(self._algorithm)
+            h.update(key_material)
+            h.update(_int_to_u32be(counter))
+            if self._sharedinfo is not None:
+                h.update(self._sharedinfo)
+            output.append(h.finalize())
+            outlen += len(output[-1])
+            counter += 1
+
+        return b"".join(output)[: self._length]
+
+    def verify(self, key_material: bytes, expected_key: bytes) -> None:
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/keywrap.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/keywrap.py
new file mode 100644
index 0000000000000000000000000000000000000000..b93d87d31cff5cecdf6513e81f8e506f2b2b6d3e
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/keywrap.py
@@ -0,0 +1,177 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography.hazmat.primitives.ciphers import Cipher
+from cryptography.hazmat.primitives.ciphers.algorithms import AES
+from cryptography.hazmat.primitives.ciphers.modes import ECB
+from cryptography.hazmat.primitives.constant_time import bytes_eq
+
+
+def _wrap_core(
+    wrapping_key: bytes,
+    a: bytes,
+    r: list[bytes],
+) -> bytes:
+    # RFC 3394 Key Wrap - 2.2.1 (index method)
+    encryptor = Cipher(AES(wrapping_key), ECB()).encryptor()
+    n = len(r)
+    for j in range(6):
+        for i in range(n):
+            # every encryption operation is a discrete 16 byte chunk (because
+            # AES has a 128-bit block size) and since we're using ECB it is
+            # safe to reuse the encryptor for the entire operation
+            b = encryptor.update(a + r[i])
+            a = (
+                int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1)
+            ).to_bytes(length=8, byteorder="big")
+            r[i] = b[-8:]
+
+    assert encryptor.finalize() == b""
+
+    return a + b"".join(r)
+
+
+def aes_key_wrap(
+    wrapping_key: bytes,
+    key_to_wrap: bytes,
+    backend: typing.Any = None,
+) -> bytes:
+    if len(wrapping_key) not in [16, 24, 32]:
+        raise ValueError("The wrapping key must be a valid AES key length")
+
+    if len(key_to_wrap) < 16:
+        raise ValueError("The key to wrap must be at least 16 bytes")
+
+    if len(key_to_wrap) % 8 != 0:
+        raise ValueError("The key to wrap must be a multiple of 8 bytes")
+
+    a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
+    r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
+    return _wrap_core(wrapping_key, a, r)
+
+
+def _unwrap_core(
+    wrapping_key: bytes,
+    a: bytes,
+    r: list[bytes],
+) -> tuple[bytes, list[bytes]]:
+    # Implement RFC 3394 Key Unwrap - 2.2.2 (index method)
+    decryptor = Cipher(AES(wrapping_key), ECB()).decryptor()
+    n = len(r)
+    for j in reversed(range(6)):
+        for i in reversed(range(n)):
+            atr = (
+                int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1)
+            ).to_bytes(length=8, byteorder="big") + r[i]
+            # every decryption operation is a discrete 16 byte chunk so
+            # it is safe to reuse the decryptor for the entire operation
+            b = decryptor.update(atr)
+            a = b[:8]
+            r[i] = b[-8:]
+
+    assert decryptor.finalize() == b""
+    return a, r
+
+
+def aes_key_wrap_with_padding(
+    wrapping_key: bytes,
+    key_to_wrap: bytes,
+    backend: typing.Any = None,
+) -> bytes:
+    if len(wrapping_key) not in [16, 24, 32]:
+        raise ValueError("The wrapping key must be a valid AES key length")
+
+    aiv = b"\xa6\x59\x59\xa6" + len(key_to_wrap).to_bytes(
+        length=4, byteorder="big"
+    )
+    # pad the key to wrap if necessary
+    pad = (8 - (len(key_to_wrap) % 8)) % 8
+    key_to_wrap = key_to_wrap + b"\x00" * pad
+    if len(key_to_wrap) == 8:
+        # RFC 5649 - 4.1 - exactly 8 octets after padding
+        encryptor = Cipher(AES(wrapping_key), ECB()).encryptor()
+        b = encryptor.update(aiv + key_to_wrap)
+        assert encryptor.finalize() == b""
+        return b
+    else:
+        r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
+        return _wrap_core(wrapping_key, aiv, r)
+
+
+def aes_key_unwrap_with_padding(
+    wrapping_key: bytes,
+    wrapped_key: bytes,
+    backend: typing.Any = None,
+) -> bytes:
+    if len(wrapped_key) < 16:
+        raise InvalidUnwrap("Must be at least 16 bytes")
+
+    if len(wrapping_key) not in [16, 24, 32]:
+        raise ValueError("The wrapping key must be a valid AES key length")
+
+    if len(wrapped_key) == 16:
+        # RFC 5649 - 4.2 - exactly two 64-bit blocks
+        decryptor = Cipher(AES(wrapping_key), ECB()).decryptor()
+        out = decryptor.update(wrapped_key)
+        assert decryptor.finalize() == b""
+        a = out[:8]
+        data = out[8:]
+        n = 1
+    else:
+        r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
+        encrypted_aiv = r.pop(0)
+        n = len(r)
+        a, r = _unwrap_core(wrapping_key, encrypted_aiv, r)
+        data = b"".join(r)
+
+    # 1) Check that MSB(32,A) = A65959A6.
+    # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n.  If so, let
+    #    MLI = LSB(32,A).
+    # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of
+    #    the output data are zero.
+    mli = int.from_bytes(a[4:], byteorder="big")
+    b = (8 * n) - mli
+    if (
+        not bytes_eq(a[:4], b"\xa6\x59\x59\xa6")
+        or not 8 * (n - 1) < mli <= 8 * n
+        or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b))
+    ):
+        raise InvalidUnwrap()
+
+    if b == 0:
+        return data
+    else:
+        return data[:-b]
+
+
+def aes_key_unwrap(
+    wrapping_key: bytes,
+    wrapped_key: bytes,
+    backend: typing.Any = None,
+) -> bytes:
+    if len(wrapped_key) < 24:
+        raise InvalidUnwrap("Must be at least 24 bytes")
+
+    if len(wrapped_key) % 8 != 0:
+        raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes")
+
+    if len(wrapping_key) not in [16, 24, 32]:
+        raise ValueError("The wrapping key must be a valid AES key length")
+
+    aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
+    r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
+    a = r.pop(0)
+    a, r = _unwrap_core(wrapping_key, a, r)
+    if not bytes_eq(a, aiv):
+        raise InvalidUnwrap()
+
+    return b"".join(r)
+
+
+class InvalidUnwrap(Exception):
+    pass
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/padding.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/padding.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1ca775f33d06d87c6d345bda692ef4f888108f7
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/padding.py
@@ -0,0 +1,204 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import typing
+
+from cryptography import utils
+from cryptography.exceptions import AlreadyFinalized
+from cryptography.hazmat.bindings._rust import (
+    PKCS7PaddingContext,
+    check_ansix923_padding,
+    check_pkcs7_padding,
+)
+
+
+class PaddingContext(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def update(self, data: bytes) -> bytes:
+        """
+        Pads the provided bytes and returns any available data as bytes.
+        """
+
+    @abc.abstractmethod
+    def finalize(self) -> bytes:
+        """
+        Finalize the padding, returns bytes.
+        """
+
+
+def _byte_padding_check(block_size: int) -> None:
+    if not (0 <= block_size <= 2040):
+        raise ValueError("block_size must be in range(0, 2041).")
+
+    if block_size % 8 != 0:
+        raise ValueError("block_size must be a multiple of 8.")
+
+
+def _byte_padding_update(
+    buffer_: bytes | None, data: bytes, block_size: int
+) -> tuple[bytes, bytes]:
+    if buffer_ is None:
+        raise AlreadyFinalized("Context was already finalized.")
+
+    utils._check_byteslike("data", data)
+
+    buffer_ += bytes(data)
+
+    finished_blocks = len(buffer_) // (block_size // 8)
+
+    result = buffer_[: finished_blocks * (block_size // 8)]
+    buffer_ = buffer_[finished_blocks * (block_size // 8) :]
+
+    return buffer_, result
+
+
+def _byte_padding_pad(
+    buffer_: bytes | None,
+    block_size: int,
+    paddingfn: typing.Callable[[int], bytes],
+) -> bytes:
+    if buffer_ is None:
+        raise AlreadyFinalized("Context was already finalized.")
+
+    pad_size = block_size // 8 - len(buffer_)
+    return buffer_ + paddingfn(pad_size)
+
+
+def _byte_unpadding_update(
+    buffer_: bytes | None, data: bytes, block_size: int
+) -> tuple[bytes, bytes]:
+    if buffer_ is None:
+        raise AlreadyFinalized("Context was already finalized.")
+
+    utils._check_byteslike("data", data)
+
+    buffer_ += bytes(data)
+
+    finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0)
+
+    result = buffer_[: finished_blocks * (block_size // 8)]
+    buffer_ = buffer_[finished_blocks * (block_size // 8) :]
+
+    return buffer_, result
+
+
+def _byte_unpadding_check(
+    buffer_: bytes | None,
+    block_size: int,
+    checkfn: typing.Callable[[bytes], int],
+) -> bytes:
+    if buffer_ is None:
+        raise AlreadyFinalized("Context was already finalized.")
+
+    if len(buffer_) != block_size // 8:
+        raise ValueError("Invalid padding bytes.")
+
+    valid = checkfn(buffer_)
+
+    if not valid:
+        raise ValueError("Invalid padding bytes.")
+
+    pad_size = buffer_[-1]
+    return buffer_[:-pad_size]
+
+
+class PKCS7:
+    def __init__(self, block_size: int):
+        _byte_padding_check(block_size)
+        self.block_size = block_size
+
+    def padder(self) -> PaddingContext:
+        return PKCS7PaddingContext(self.block_size)
+
+    def unpadder(self) -> PaddingContext:
+        return _PKCS7UnpaddingContext(self.block_size)
+
+
+class _PKCS7UnpaddingContext(PaddingContext):
+    _buffer: bytes | None
+
+    def __init__(self, block_size: int):
+        self.block_size = block_size
+        # TODO: more copies than necessary, we should use zero-buffer (#193)
+        self._buffer = b""
+
+    def update(self, data: bytes) -> bytes:
+        self._buffer, result = _byte_unpadding_update(
+            self._buffer, data, self.block_size
+        )
+        return result
+
+    def finalize(self) -> bytes:
+        result = _byte_unpadding_check(
+            self._buffer, self.block_size, check_pkcs7_padding
+        )
+        self._buffer = None
+        return result
+
+
+PaddingContext.register(PKCS7PaddingContext)
+
+
+class ANSIX923:
+    def __init__(self, block_size: int):
+        _byte_padding_check(block_size)
+        self.block_size = block_size
+
+    def padder(self) -> PaddingContext:
+        return _ANSIX923PaddingContext(self.block_size)
+
+    def unpadder(self) -> PaddingContext:
+        return _ANSIX923UnpaddingContext(self.block_size)
+
+
+class _ANSIX923PaddingContext(PaddingContext):
+    _buffer: bytes | None
+
+    def __init__(self, block_size: int):
+        self.block_size = block_size
+        # TODO: more copies than necessary, we should use zero-buffer (#193)
+        self._buffer = b""
+
+    def update(self, data: bytes) -> bytes:
+        self._buffer, result = _byte_padding_update(
+            self._buffer, data, self.block_size
+        )
+        return result
+
+    def _padding(self, size: int) -> bytes:
+        return bytes([0]) * (size - 1) + bytes([size])
+
+    def finalize(self) -> bytes:
+        result = _byte_padding_pad(
+            self._buffer, self.block_size, self._padding
+        )
+        self._buffer = None
+        return result
+
+
+class _ANSIX923UnpaddingContext(PaddingContext):
+    _buffer: bytes | None
+
+    def __init__(self, block_size: int):
+        self.block_size = block_size
+        # TODO: more copies than necessary, we should use zero-buffer (#193)
+        self._buffer = b""
+
+    def update(self, data: bytes) -> bytes:
+        self._buffer, result = _byte_unpadding_update(
+            self._buffer, data, self.block_size
+        )
+        return result
+
+    def finalize(self) -> bytes:
+        result = _byte_unpadding_check(
+            self._buffer,
+            self.block_size,
+            check_ansix923_padding,
+        )
+        self._buffer = None
+        return result
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/poly1305.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/poly1305.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f5a77a576fd258a9a634ae3c3365fc3f7d4702a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/poly1305.py
@@ -0,0 +1,11 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+
+__all__ = ["Poly1305"]
+
+Poly1305 = rust_openssl.poly1305.Poly1305
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..07b2264b9a5136c315995243e3d5eb9ba8d95fe4
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/__init__.py
@@ -0,0 +1,63 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat.primitives._serialization import (
+    BestAvailableEncryption,
+    Encoding,
+    KeySerializationEncryption,
+    NoEncryption,
+    ParameterFormat,
+    PrivateFormat,
+    PublicFormat,
+    _KeySerializationEncryption,
+)
+from cryptography.hazmat.primitives.serialization.base import (
+    load_der_parameters,
+    load_der_private_key,
+    load_der_public_key,
+    load_pem_parameters,
+    load_pem_private_key,
+    load_pem_public_key,
+)
+from cryptography.hazmat.primitives.serialization.ssh import (
+    SSHCertificate,
+    SSHCertificateBuilder,
+    SSHCertificateType,
+    SSHCertPrivateKeyTypes,
+    SSHCertPublicKeyTypes,
+    SSHPrivateKeyTypes,
+    SSHPublicKeyTypes,
+    load_ssh_private_key,
+    load_ssh_public_identity,
+    load_ssh_public_key,
+)
+
+__all__ = [
+    "BestAvailableEncryption",
+    "Encoding",
+    "KeySerializationEncryption",
+    "NoEncryption",
+    "ParameterFormat",
+    "PrivateFormat",
+    "PublicFormat",
+    "SSHCertPrivateKeyTypes",
+    "SSHCertPublicKeyTypes",
+    "SSHCertificate",
+    "SSHCertificateBuilder",
+    "SSHCertificateType",
+    "SSHPrivateKeyTypes",
+    "SSHPublicKeyTypes",
+    "_KeySerializationEncryption",
+    "load_der_parameters",
+    "load_der_private_key",
+    "load_der_public_key",
+    "load_pem_parameters",
+    "load_pem_private_key",
+    "load_pem_public_key",
+    "load_ssh_private_key",
+    "load_ssh_public_identity",
+    "load_ssh_public_key",
+]
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/base.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7c998b7f35b3e364909348f0516f51cf902ebfe
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/base.py
@@ -0,0 +1,14 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from cryptography.hazmat.bindings._rust import openssl as rust_openssl
+
+load_pem_private_key = rust_openssl.keys.load_pem_private_key
+load_der_private_key = rust_openssl.keys.load_der_private_key
+
+load_pem_public_key = rust_openssl.keys.load_pem_public_key
+load_der_public_key = rust_openssl.keys.load_der_public_key
+
+load_pem_parameters = rust_openssl.dh.from_pem_parameters
+load_der_parameters = rust_openssl.dh.from_der_parameters
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py
new file mode 100644
index 0000000000000000000000000000000000000000..549e1f992d396c56208f974e65190216ac623545
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py
@@ -0,0 +1,156 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography import x509
+from cryptography.hazmat.bindings._rust import pkcs12 as rust_pkcs12
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives._serialization import PBES as PBES
+from cryptography.hazmat.primitives.asymmetric import (
+    dsa,
+    ec,
+    ed448,
+    ed25519,
+    rsa,
+)
+from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
+
+__all__ = [
+    "PBES",
+    "PKCS12Certificate",
+    "PKCS12KeyAndCertificates",
+    "PKCS12PrivateKeyTypes",
+    "load_key_and_certificates",
+    "load_pkcs12",
+    "serialize_key_and_certificates",
+]
+
+PKCS12PrivateKeyTypes = typing.Union[
+    rsa.RSAPrivateKey,
+    dsa.DSAPrivateKey,
+    ec.EllipticCurvePrivateKey,
+    ed25519.Ed25519PrivateKey,
+    ed448.Ed448PrivateKey,
+]
+
+
+PKCS12Certificate = rust_pkcs12.PKCS12Certificate
+
+
+class PKCS12KeyAndCertificates:
+    def __init__(
+        self,
+        key: PrivateKeyTypes | None,
+        cert: PKCS12Certificate | None,
+        additional_certs: list[PKCS12Certificate],
+    ):
+        if key is not None and not isinstance(
+            key,
+            (
+                rsa.RSAPrivateKey,
+                dsa.DSAPrivateKey,
+                ec.EllipticCurvePrivateKey,
+                ed25519.Ed25519PrivateKey,
+                ed448.Ed448PrivateKey,
+            ),
+        ):
+            raise TypeError(
+                "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448"
+                " private key, or None."
+            )
+        if cert is not None and not isinstance(cert, PKCS12Certificate):
+            raise TypeError("cert must be a PKCS12Certificate object or None")
+        if not all(
+            isinstance(add_cert, PKCS12Certificate)
+            for add_cert in additional_certs
+        ):
+            raise TypeError(
+                "all values in additional_certs must be PKCS12Certificate"
+                " objects"
+            )
+        self._key = key
+        self._cert = cert
+        self._additional_certs = additional_certs
+
+    @property
+    def key(self) -> PrivateKeyTypes | None:
+        return self._key
+
+    @property
+    def cert(self) -> PKCS12Certificate | None:
+        return self._cert
+
+    @property
+    def additional_certs(self) -> list[PKCS12Certificate]:
+        return self._additional_certs
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, PKCS12KeyAndCertificates):
+            return NotImplemented
+
+        return (
+            self.key == other.key
+            and self.cert == other.cert
+            and self.additional_certs == other.additional_certs
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.key, self.cert, tuple(self.additional_certs)))
+
+    def __repr__(self) -> str:
+        fmt = (
+            "<PKCS12KeyAndCertificates(key={}, cert={}, additional_certs={})>"
+        )
+        return fmt.format(self.key, self.cert, self.additional_certs)
+
+
+load_key_and_certificates = rust_pkcs12.load_key_and_certificates
+load_pkcs12 = rust_pkcs12.load_pkcs12
+
+
+_PKCS12CATypes = typing.Union[
+    x509.Certificate,
+    PKCS12Certificate,
+]
+
+
+def serialize_key_and_certificates(
+    name: bytes | None,
+    key: PKCS12PrivateKeyTypes | None,
+    cert: x509.Certificate | None,
+    cas: typing.Iterable[_PKCS12CATypes] | None,
+    encryption_algorithm: serialization.KeySerializationEncryption,
+) -> bytes:
+    if key is not None and not isinstance(
+        key,
+        (
+            rsa.RSAPrivateKey,
+            dsa.DSAPrivateKey,
+            ec.EllipticCurvePrivateKey,
+            ed25519.Ed25519PrivateKey,
+            ed448.Ed448PrivateKey,
+        ),
+    ):
+        raise TypeError(
+            "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448"
+            " private key, or None."
+        )
+
+    if not isinstance(
+        encryption_algorithm, serialization.KeySerializationEncryption
+    ):
+        raise TypeError(
+            "Key encryption algorithm must be a "
+            "KeySerializationEncryption instance"
+        )
+
+    if key is None and cert is None and not cas:
+        raise ValueError("You must supply at least one of key, cert, or cas")
+
+    return rust_pkcs12.serialize_key_and_certificates(
+        name, key, cert, cas, encryption_algorithm
+    )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py
new file mode 100644
index 0000000000000000000000000000000000000000..97ea9db8e17176ba3de41c3cc0002911fe3fb7ac
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py
@@ -0,0 +1,336 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import email.base64mime
+import email.generator
+import email.message
+import email.policy
+import io
+import typing
+
+from cryptography import utils, x509
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa
+from cryptography.utils import _check_byteslike
+
+load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates
+
+load_der_pkcs7_certificates = rust_pkcs7.load_der_pkcs7_certificates
+
+serialize_certificates = rust_pkcs7.serialize_certificates
+
+PKCS7HashTypes = typing.Union[
+    hashes.SHA224,
+    hashes.SHA256,
+    hashes.SHA384,
+    hashes.SHA512,
+]
+
+PKCS7PrivateKeyTypes = typing.Union[
+    rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey
+]
+
+
+class PKCS7Options(utils.Enum):
+    Text = "Add text/plain MIME type"
+    Binary = "Don't translate input data into canonical MIME format"
+    DetachedSignature = "Don't embed data in the PKCS7 structure"
+    NoCapabilities = "Don't embed SMIME capabilities"
+    NoAttributes = "Don't embed authenticatedAttributes"
+    NoCerts = "Don't embed signer certificate"
+
+
+class PKCS7SignatureBuilder:
+    def __init__(
+        self,
+        data: bytes | None = None,
+        signers: list[
+            tuple[
+                x509.Certificate,
+                PKCS7PrivateKeyTypes,
+                PKCS7HashTypes,
+                padding.PSS | padding.PKCS1v15 | None,
+            ]
+        ] = [],
+        additional_certs: list[x509.Certificate] = [],
+    ):
+        self._data = data
+        self._signers = signers
+        self._additional_certs = additional_certs
+
+    def set_data(self, data: bytes) -> PKCS7SignatureBuilder:
+        _check_byteslike("data", data)
+        if self._data is not None:
+            raise ValueError("data may only be set once")
+
+        return PKCS7SignatureBuilder(data, self._signers)
+
+    def add_signer(
+        self,
+        certificate: x509.Certificate,
+        private_key: PKCS7PrivateKeyTypes,
+        hash_algorithm: PKCS7HashTypes,
+        *,
+        rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+    ) -> PKCS7SignatureBuilder:
+        if not isinstance(
+            hash_algorithm,
+            (
+                hashes.SHA224,
+                hashes.SHA256,
+                hashes.SHA384,
+                hashes.SHA512,
+            ),
+        ):
+            raise TypeError(
+                "hash_algorithm must be one of hashes.SHA224, "
+                "SHA256, SHA384, or SHA512"
+            )
+        if not isinstance(certificate, x509.Certificate):
+            raise TypeError("certificate must be a x509.Certificate")
+
+        if not isinstance(
+            private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
+        ):
+            raise TypeError("Only RSA & EC keys are supported at this time.")
+
+        if rsa_padding is not None:
+            if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+                raise TypeError("Padding must be PSS or PKCS1v15")
+            if not isinstance(private_key, rsa.RSAPrivateKey):
+                raise TypeError("Padding is only supported for RSA keys")
+
+        return PKCS7SignatureBuilder(
+            self._data,
+            [
+                *self._signers,
+                (certificate, private_key, hash_algorithm, rsa_padding),
+            ],
+        )
+
+    def add_certificate(
+        self, certificate: x509.Certificate
+    ) -> PKCS7SignatureBuilder:
+        if not isinstance(certificate, x509.Certificate):
+            raise TypeError("certificate must be a x509.Certificate")
+
+        return PKCS7SignatureBuilder(
+            self._data, self._signers, [*self._additional_certs, certificate]
+        )
+
+    def sign(
+        self,
+        encoding: serialization.Encoding,
+        options: typing.Iterable[PKCS7Options],
+        backend: typing.Any = None,
+    ) -> bytes:
+        if len(self._signers) == 0:
+            raise ValueError("Must have at least one signer")
+        if self._data is None:
+            raise ValueError("You must add data to sign")
+        options = list(options)
+        if not all(isinstance(x, PKCS7Options) for x in options):
+            raise ValueError("options must be from the PKCS7Options enum")
+        if encoding not in (
+            serialization.Encoding.PEM,
+            serialization.Encoding.DER,
+            serialization.Encoding.SMIME,
+        ):
+            raise ValueError(
+                "Must be PEM, DER, or SMIME from the Encoding enum"
+            )
+
+        # Text is a meaningless option unless it is accompanied by
+        # DetachedSignature
+        if (
+            PKCS7Options.Text in options
+            and PKCS7Options.DetachedSignature not in options
+        ):
+            raise ValueError(
+                "When passing the Text option you must also pass "
+                "DetachedSignature"
+            )
+
+        if PKCS7Options.Text in options and encoding in (
+            serialization.Encoding.DER,
+            serialization.Encoding.PEM,
+        ):
+            raise ValueError(
+                "The Text option is only available for SMIME serialization"
+            )
+
+        # No attributes implies no capabilities so we'll error if you try to
+        # pass both.
+        if (
+            PKCS7Options.NoAttributes in options
+            and PKCS7Options.NoCapabilities in options
+        ):
+            raise ValueError(
+                "NoAttributes is a superset of NoCapabilities. Do not pass "
+                "both values."
+            )
+
+        return rust_pkcs7.sign_and_serialize(self, encoding, options)
+
+
+class PKCS7EnvelopeBuilder:
+    def __init__(
+        self,
+        *,
+        _data: bytes | None = None,
+        _recipients: list[x509.Certificate] | None = None,
+    ):
+        from cryptography.hazmat.backends.openssl.backend import (
+            backend as ossl,
+        )
+
+        if not ossl.rsa_encryption_supported(padding=padding.PKCS1v15()):
+            raise UnsupportedAlgorithm(
+                "RSA with PKCS1 v1.5 padding is not supported by this version"
+                " of OpenSSL.",
+                _Reasons.UNSUPPORTED_PADDING,
+            )
+        self._data = _data
+        self._recipients = _recipients if _recipients is not None else []
+
+    def set_data(self, data: bytes) -> PKCS7EnvelopeBuilder:
+        _check_byteslike("data", data)
+        if self._data is not None:
+            raise ValueError("data may only be set once")
+
+        return PKCS7EnvelopeBuilder(_data=data, _recipients=self._recipients)
+
+    def add_recipient(
+        self,
+        certificate: x509.Certificate,
+    ) -> PKCS7EnvelopeBuilder:
+        if not isinstance(certificate, x509.Certificate):
+            raise TypeError("certificate must be a x509.Certificate")
+
+        if not isinstance(certificate.public_key(), rsa.RSAPublicKey):
+            raise TypeError("Only RSA keys are supported at this time.")
+
+        return PKCS7EnvelopeBuilder(
+            _data=self._data,
+            _recipients=[
+                *self._recipients,
+                certificate,
+            ],
+        )
+
+    def encrypt(
+        self,
+        encoding: serialization.Encoding,
+        options: typing.Iterable[PKCS7Options],
+    ) -> bytes:
+        if len(self._recipients) == 0:
+            raise ValueError("Must have at least one recipient")
+        if self._data is None:
+            raise ValueError("You must add data to encrypt")
+        options = list(options)
+        if not all(isinstance(x, PKCS7Options) for x in options):
+            raise ValueError("options must be from the PKCS7Options enum")
+        if encoding not in (
+            serialization.Encoding.PEM,
+            serialization.Encoding.DER,
+            serialization.Encoding.SMIME,
+        ):
+            raise ValueError(
+                "Must be PEM, DER, or SMIME from the Encoding enum"
+            )
+
+        # Only allow options that make sense for encryption
+        if any(
+            opt not in [PKCS7Options.Text, PKCS7Options.Binary]
+            for opt in options
+        ):
+            raise ValueError(
+                "Only the following options are supported for encryption: "
+                "Text, Binary"
+            )
+        elif PKCS7Options.Text in options and PKCS7Options.Binary in options:
+            # OpenSSL accepts both options at the same time, but ignores Text.
+            # We fail defensively to avoid unexpected outputs.
+            raise ValueError(
+                "Cannot use Binary and Text options at the same time"
+            )
+
+        return rust_pkcs7.encrypt_and_serialize(self, encoding, options)
+
+
+def _smime_signed_encode(
+    data: bytes, signature: bytes, micalg: str, text_mode: bool
+) -> bytes:
+    # This function works pretty hard to replicate what OpenSSL does
+    # precisely. For good and for ill.
+
+    m = email.message.Message()
+    m.add_header("MIME-Version", "1.0")
+    m.add_header(
+        "Content-Type",
+        "multipart/signed",
+        protocol="application/x-pkcs7-signature",
+        micalg=micalg,
+    )
+
+    m.preamble = "This is an S/MIME signed message\n"
+
+    msg_part = OpenSSLMimePart()
+    msg_part.set_payload(data)
+    if text_mode:
+        msg_part.add_header("Content-Type", "text/plain")
+    m.attach(msg_part)
+
+    sig_part = email.message.MIMEPart()
+    sig_part.add_header(
+        "Content-Type", "application/x-pkcs7-signature", name="smime.p7s"
+    )
+    sig_part.add_header("Content-Transfer-Encoding", "base64")
+    sig_part.add_header(
+        "Content-Disposition", "attachment", filename="smime.p7s"
+    )
+    sig_part.set_payload(
+        email.base64mime.body_encode(signature, maxlinelen=65)
+    )
+    del sig_part["MIME-Version"]
+    m.attach(sig_part)
+
+    fp = io.BytesIO()
+    g = email.generator.BytesGenerator(
+        fp,
+        maxheaderlen=0,
+        mangle_from_=False,
+        policy=m.policy.clone(linesep="\r\n"),
+    )
+    g.flatten(m)
+    return fp.getvalue()
+
+
+def _smime_enveloped_encode(data: bytes) -> bytes:
+    m = email.message.Message()
+    m.add_header("MIME-Version", "1.0")
+    m.add_header("Content-Disposition", "attachment", filename="smime.p7m")
+    m.add_header(
+        "Content-Type",
+        "application/pkcs7-mime",
+        smime_type="enveloped-data",
+        name="smime.p7m",
+    )
+    m.add_header("Content-Transfer-Encoding", "base64")
+
+    m.set_payload(email.base64mime.body_encode(data, maxlinelen=65))
+
+    return m.as_bytes(policy=m.policy.clone(linesep="\n", max_line_length=0))
+
+
+class OpenSSLMimePart(email.message.MIMEPart):
+    # A MIMEPart subclass that replicates OpenSSL's behavior of not including
+    # a newline if there are no headers.
+    def _write_headers(self, generator) -> None:
+        if list(self.raw_items()):
+            generator._write_headers(self)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/ssh.py
new file mode 100644
index 0000000000000000000000000000000000000000..c01afb0ccdc9594729e3d0eb8d4f9da9d1933834
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/serialization/ssh.py
@@ -0,0 +1,1569 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import binascii
+import enum
+import os
+import re
+import typing
+import warnings
+from base64 import encodebytes as _base64_encode
+from dataclasses import dataclass
+
+from cryptography import utils
+from cryptography.exceptions import UnsupportedAlgorithm
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric import (
+    dsa,
+    ec,
+    ed25519,
+    padding,
+    rsa,
+)
+from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
+from cryptography.hazmat.primitives.ciphers import (
+    AEADDecryptionContext,
+    Cipher,
+    algorithms,
+    modes,
+)
+from cryptography.hazmat.primitives.serialization import (
+    Encoding,
+    KeySerializationEncryption,
+    NoEncryption,
+    PrivateFormat,
+    PublicFormat,
+    _KeySerializationEncryption,
+)
+
+try:
+    from bcrypt import kdf as _bcrypt_kdf
+
+    _bcrypt_supported = True
+except ImportError:
+    _bcrypt_supported = False
+
+    def _bcrypt_kdf(
+        password: bytes,
+        salt: bytes,
+        desired_key_bytes: int,
+        rounds: int,
+        ignore_few_rounds: bool = False,
+    ) -> bytes:
+        raise UnsupportedAlgorithm("Need bcrypt module")
+
+
+_SSH_ED25519 = b"ssh-ed25519"
+_SSH_RSA = b"ssh-rsa"
+_SSH_DSA = b"ssh-dss"
+_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256"
+_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384"
+_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
+_CERT_SUFFIX = b"-cert-v01@openssh.com"
+
+# U2F application string suffixed pubkey
+_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com"
+_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com"
+
+# These are not key types, only algorithms, so they cannot appear
+# as a public key type
+_SSH_RSA_SHA256 = b"rsa-sha2-256"
+_SSH_RSA_SHA512 = b"rsa-sha2-512"
+
+_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)")
+_SK_MAGIC = b"openssh-key-v1\0"
+_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
+_SK_END = b"-----END OPENSSH PRIVATE KEY-----"
+_BCRYPT = b"bcrypt"
+_NONE = b"none"
+_DEFAULT_CIPHER = b"aes256-ctr"
+_DEFAULT_ROUNDS = 16
+
+# re is only way to work on bytes-like data
+_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL)
+
+# padding for max blocksize
+_PADDING = memoryview(bytearray(range(1, 1 + 16)))
+
+
+@dataclass
+class _SSHCipher:
+    alg: type[algorithms.AES]
+    key_len: int
+    mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM]
+    block_len: int
+    iv_len: int
+    tag_len: int | None
+    is_aead: bool
+
+
+# ciphers that are actually used in key wrapping
+_SSH_CIPHERS: dict[bytes, _SSHCipher] = {
+    b"aes256-ctr": _SSHCipher(
+        alg=algorithms.AES,
+        key_len=32,
+        mode=modes.CTR,
+        block_len=16,
+        iv_len=16,
+        tag_len=None,
+        is_aead=False,
+    ),
+    b"aes256-cbc": _SSHCipher(
+        alg=algorithms.AES,
+        key_len=32,
+        mode=modes.CBC,
+        block_len=16,
+        iv_len=16,
+        tag_len=None,
+        is_aead=False,
+    ),
+    b"aes256-gcm@openssh.com": _SSHCipher(
+        alg=algorithms.AES,
+        key_len=32,
+        mode=modes.GCM,
+        block_len=16,
+        iv_len=12,
+        tag_len=16,
+        is_aead=True,
+    ),
+}
+
+# map local curve name to key type
+_ECDSA_KEY_TYPE = {
+    "secp256r1": _ECDSA_NISTP256,
+    "secp384r1": _ECDSA_NISTP384,
+    "secp521r1": _ECDSA_NISTP521,
+}
+
+
+def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes:
+    if isinstance(key, ec.EllipticCurvePrivateKey):
+        key_type = _ecdsa_key_type(key.public_key())
+    elif isinstance(key, ec.EllipticCurvePublicKey):
+        key_type = _ecdsa_key_type(key)
+    elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
+        key_type = _SSH_RSA
+    elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)):
+        key_type = _SSH_DSA
+    elif isinstance(
+        key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey)
+    ):
+        key_type = _SSH_ED25519
+    else:
+        raise ValueError("Unsupported key type")
+
+    return key_type
+
+
+def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes:
+    """Return SSH key_type and curve_name for private key."""
+    curve = public_key.curve
+    if curve.name not in _ECDSA_KEY_TYPE:
+        raise ValueError(
+            f"Unsupported curve for ssh private key: {curve.name!r}"
+        )
+    return _ECDSA_KEY_TYPE[curve.name]
+
+
+def _ssh_pem_encode(
+    data: bytes,
+    prefix: bytes = _SK_START + b"\n",
+    suffix: bytes = _SK_END + b"\n",
+) -> bytes:
+    return b"".join([prefix, _base64_encode(data), suffix])
+
+
+def _check_block_size(data: bytes, block_len: int) -> None:
+    """Require data to be full blocks"""
+    if not data or len(data) % block_len != 0:
+        raise ValueError("Corrupt data: missing padding")
+
+
+def _check_empty(data: bytes) -> None:
+    """All data should have been parsed."""
+    if data:
+        raise ValueError("Corrupt data: unparsed data")
+
+
+def _init_cipher(
+    ciphername: bytes,
+    password: bytes | None,
+    salt: bytes,
+    rounds: int,
+) -> Cipher[modes.CBC | modes.CTR | modes.GCM]:
+    """Generate key + iv and return cipher."""
+    if not password:
+        raise ValueError("Key is password-protected.")
+
+    ciph = _SSH_CIPHERS[ciphername]
+    seed = _bcrypt_kdf(
+        password, salt, ciph.key_len + ciph.iv_len, rounds, True
+    )
+    return Cipher(
+        ciph.alg(seed[: ciph.key_len]),
+        ciph.mode(seed[ciph.key_len :]),
+    )
+
+
+def _get_u32(data: memoryview) -> tuple[int, memoryview]:
+    """Uint32"""
+    if len(data) < 4:
+        raise ValueError("Invalid data")
+    return int.from_bytes(data[:4], byteorder="big"), data[4:]
+
+
+def _get_u64(data: memoryview) -> tuple[int, memoryview]:
+    """Uint64"""
+    if len(data) < 8:
+        raise ValueError("Invalid data")
+    return int.from_bytes(data[:8], byteorder="big"), data[8:]
+
+
+def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]:
+    """Bytes with u32 length prefix"""
+    n, data = _get_u32(data)
+    if n > len(data):
+        raise ValueError("Invalid data")
+    return data[:n], data[n:]
+
+
+def _get_mpint(data: memoryview) -> tuple[int, memoryview]:
+    """Big integer."""
+    val, data = _get_sshstr(data)
+    if val and val[0] > 0x7F:
+        raise ValueError("Invalid data")
+    return int.from_bytes(val, "big"), data
+
+
+def _to_mpint(val: int) -> bytes:
+    """Storage format for signed bigint."""
+    if val < 0:
+        raise ValueError("negative mpint not allowed")
+    if not val:
+        return b""
+    nbytes = (val.bit_length() + 8) // 8
+    return utils.int_to_bytes(val, nbytes)
+
+
+class _FragList:
+    """Build recursive structure without data copy."""
+
+    flist: list[bytes]
+
+    def __init__(self, init: list[bytes] | None = None) -> None:
+        self.flist = []
+        if init:
+            self.flist.extend(init)
+
+    def put_raw(self, val: bytes) -> None:
+        """Add plain bytes"""
+        self.flist.append(val)
+
+    def put_u32(self, val: int) -> None:
+        """Big-endian uint32"""
+        self.flist.append(val.to_bytes(length=4, byteorder="big"))
+
+    def put_u64(self, val: int) -> None:
+        """Big-endian uint64"""
+        self.flist.append(val.to_bytes(length=8, byteorder="big"))
+
+    def put_sshstr(self, val: bytes | _FragList) -> None:
+        """Bytes prefixed with u32 length"""
+        if isinstance(val, (bytes, memoryview, bytearray)):
+            self.put_u32(len(val))
+            self.flist.append(val)
+        else:
+            self.put_u32(val.size())
+            self.flist.extend(val.flist)
+
+    def put_mpint(self, val: int) -> None:
+        """Big-endian bigint prefixed with u32 length"""
+        self.put_sshstr(_to_mpint(val))
+
+    def size(self) -> int:
+        """Current number of bytes"""
+        return sum(map(len, self.flist))
+
+    def render(self, dstbuf: memoryview, pos: int = 0) -> int:
+        """Write into bytearray"""
+        for frag in self.flist:
+            flen = len(frag)
+            start, pos = pos, pos + flen
+            dstbuf[start:pos] = frag
+        return pos
+
+    def tobytes(self) -> bytes:
+        """Return as bytes"""
+        buf = memoryview(bytearray(self.size()))
+        self.render(buf)
+        return buf.tobytes()
+
+
+class _SSHFormatRSA:
+    """Format for RSA keys.
+
+    Public:
+        mpint e, n
+    Private:
+        mpint n, e, d, iqmp, p, q
+    """
+
+    def get_public(
+        self, data: memoryview
+    ) -> tuple[tuple[int, int], memoryview]:
+        """RSA public fields"""
+        e, data = _get_mpint(data)
+        n, data = _get_mpint(data)
+        return (e, n), data
+
+    def load_public(
+        self, data: memoryview
+    ) -> tuple[rsa.RSAPublicKey, memoryview]:
+        """Make RSA public key from data."""
+        (e, n), data = self.get_public(data)
+        public_numbers = rsa.RSAPublicNumbers(e, n)
+        public_key = public_numbers.public_key()
+        return public_key, data
+
+    def load_private(
+        self, data: memoryview, pubfields
+    ) -> tuple[rsa.RSAPrivateKey, memoryview]:
+        """Make RSA private key from data."""
+        n, data = _get_mpint(data)
+        e, data = _get_mpint(data)
+        d, data = _get_mpint(data)
+        iqmp, data = _get_mpint(data)
+        p, data = _get_mpint(data)
+        q, data = _get_mpint(data)
+
+        if (e, n) != pubfields:
+            raise ValueError("Corrupt data: rsa field mismatch")
+        dmp1 = rsa.rsa_crt_dmp1(d, p)
+        dmq1 = rsa.rsa_crt_dmq1(d, q)
+        public_numbers = rsa.RSAPublicNumbers(e, n)
+        private_numbers = rsa.RSAPrivateNumbers(
+            p, q, d, dmp1, dmq1, iqmp, public_numbers
+        )
+        private_key = private_numbers.private_key()
+        return private_key, data
+
+    def encode_public(
+        self, public_key: rsa.RSAPublicKey, f_pub: _FragList
+    ) -> None:
+        """Write RSA public key"""
+        pubn = public_key.public_numbers()
+        f_pub.put_mpint(pubn.e)
+        f_pub.put_mpint(pubn.n)
+
+    def encode_private(
+        self, private_key: rsa.RSAPrivateKey, f_priv: _FragList
+    ) -> None:
+        """Write RSA private key"""
+        private_numbers = private_key.private_numbers()
+        public_numbers = private_numbers.public_numbers
+
+        f_priv.put_mpint(public_numbers.n)
+        f_priv.put_mpint(public_numbers.e)
+
+        f_priv.put_mpint(private_numbers.d)
+        f_priv.put_mpint(private_numbers.iqmp)
+        f_priv.put_mpint(private_numbers.p)
+        f_priv.put_mpint(private_numbers.q)
+
+
+class _SSHFormatDSA:
+    """Format for DSA keys.
+
+    Public:
+        mpint p, q, g, y
+    Private:
+        mpint p, q, g, y, x
+    """
+
+    def get_public(self, data: memoryview) -> tuple[tuple, memoryview]:
+        """DSA public fields"""
+        p, data = _get_mpint(data)
+        q, data = _get_mpint(data)
+        g, data = _get_mpint(data)
+        y, data = _get_mpint(data)
+        return (p, q, g, y), data
+
+    def load_public(
+        self, data: memoryview
+    ) -> tuple[dsa.DSAPublicKey, memoryview]:
+        """Make DSA public key from data."""
+        (p, q, g, y), data = self.get_public(data)
+        parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
+        public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+        self._validate(public_numbers)
+        public_key = public_numbers.public_key()
+        return public_key, data
+
+    def load_private(
+        self, data: memoryview, pubfields
+    ) -> tuple[dsa.DSAPrivateKey, memoryview]:
+        """Make DSA private key from data."""
+        (p, q, g, y), data = self.get_public(data)
+        x, data = _get_mpint(data)
+
+        if (p, q, g, y) != pubfields:
+            raise ValueError("Corrupt data: dsa field mismatch")
+        parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
+        public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+        self._validate(public_numbers)
+        private_numbers = dsa.DSAPrivateNumbers(x, public_numbers)
+        private_key = private_numbers.private_key()
+        return private_key, data
+
+    def encode_public(
+        self, public_key: dsa.DSAPublicKey, f_pub: _FragList
+    ) -> None:
+        """Write DSA public key"""
+        public_numbers = public_key.public_numbers()
+        parameter_numbers = public_numbers.parameter_numbers
+        self._validate(public_numbers)
+
+        f_pub.put_mpint(parameter_numbers.p)
+        f_pub.put_mpint(parameter_numbers.q)
+        f_pub.put_mpint(parameter_numbers.g)
+        f_pub.put_mpint(public_numbers.y)
+
+    def encode_private(
+        self, private_key: dsa.DSAPrivateKey, f_priv: _FragList
+    ) -> None:
+        """Write DSA private key"""
+        self.encode_public(private_key.public_key(), f_priv)
+        f_priv.put_mpint(private_key.private_numbers().x)
+
+    def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None:
+        parameter_numbers = public_numbers.parameter_numbers
+        if parameter_numbers.p.bit_length() != 1024:
+            raise ValueError("SSH supports only 1024 bit DSA keys")
+
+
+class _SSHFormatECDSA:
+    """Format for ECDSA keys.
+
+    Public:
+        str curve
+        bytes point
+    Private:
+        str curve
+        bytes point
+        mpint secret
+    """
+
+    def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve):
+        self.ssh_curve_name = ssh_curve_name
+        self.curve = curve
+
+    def get_public(
+        self, data: memoryview
+    ) -> tuple[tuple[memoryview, memoryview], memoryview]:
+        """ECDSA public fields"""
+        curve, data = _get_sshstr(data)
+        point, data = _get_sshstr(data)
+        if curve != self.ssh_curve_name:
+            raise ValueError("Curve name mismatch")
+        if point[0] != 4:
+            raise NotImplementedError("Need uncompressed point")
+        return (curve, point), data
+
+    def load_public(
+        self, data: memoryview
+    ) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
+        """Make ECDSA public key from data."""
+        (_, point), data = self.get_public(data)
+        public_key = ec.EllipticCurvePublicKey.from_encoded_point(
+            self.curve, point.tobytes()
+        )
+        return public_key, data
+
+    def load_private(
+        self, data: memoryview, pubfields
+    ) -> tuple[ec.EllipticCurvePrivateKey, memoryview]:
+        """Make ECDSA private key from data."""
+        (curve_name, point), data = self.get_public(data)
+        secret, data = _get_mpint(data)
+
+        if (curve_name, point) != pubfields:
+            raise ValueError("Corrupt data: ecdsa field mismatch")
+        private_key = ec.derive_private_key(secret, self.curve)
+        return private_key, data
+
+    def encode_public(
+        self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList
+    ) -> None:
+        """Write ECDSA public key"""
+        point = public_key.public_bytes(
+            Encoding.X962, PublicFormat.UncompressedPoint
+        )
+        f_pub.put_sshstr(self.ssh_curve_name)
+        f_pub.put_sshstr(point)
+
+    def encode_private(
+        self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList
+    ) -> None:
+        """Write ECDSA private key"""
+        public_key = private_key.public_key()
+        private_numbers = private_key.private_numbers()
+
+        self.encode_public(public_key, f_priv)
+        f_priv.put_mpint(private_numbers.private_value)
+
+
+class _SSHFormatEd25519:
+    """Format for Ed25519 keys.
+
+    Public:
+        bytes point
+    Private:
+        bytes point
+        bytes secret_and_point
+    """
+
+    def get_public(
+        self, data: memoryview
+    ) -> tuple[tuple[memoryview], memoryview]:
+        """Ed25519 public fields"""
+        point, data = _get_sshstr(data)
+        return (point,), data
+
+    def load_public(
+        self, data: memoryview
+    ) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
+        """Make Ed25519 public key from data."""
+        (point,), data = self.get_public(data)
+        public_key = ed25519.Ed25519PublicKey.from_public_bytes(
+            point.tobytes()
+        )
+        return public_key, data
+
+    def load_private(
+        self, data: memoryview, pubfields
+    ) -> tuple[ed25519.Ed25519PrivateKey, memoryview]:
+        """Make Ed25519 private key from data."""
+        (point,), data = self.get_public(data)
+        keypair, data = _get_sshstr(data)
+
+        secret = keypair[:32]
+        point2 = keypair[32:]
+        if point != point2 or (point,) != pubfields:
+            raise ValueError("Corrupt data: ed25519 field mismatch")
+        private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret)
+        return private_key, data
+
+    def encode_public(
+        self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList
+    ) -> None:
+        """Write Ed25519 public key"""
+        raw_public_key = public_key.public_bytes(
+            Encoding.Raw, PublicFormat.Raw
+        )
+        f_pub.put_sshstr(raw_public_key)
+
+    def encode_private(
+        self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList
+    ) -> None:
+        """Write Ed25519 private key"""
+        public_key = private_key.public_key()
+        raw_private_key = private_key.private_bytes(
+            Encoding.Raw, PrivateFormat.Raw, NoEncryption()
+        )
+        raw_public_key = public_key.public_bytes(
+            Encoding.Raw, PublicFormat.Raw
+        )
+        f_keypair = _FragList([raw_private_key, raw_public_key])
+
+        self.encode_public(public_key, f_priv)
+        f_priv.put_sshstr(f_keypair)
+
+
+def load_application(data) -> tuple[memoryview, memoryview]:
+    """
+    U2F application strings
+    """
+    application, data = _get_sshstr(data)
+    if not application.tobytes().startswith(b"ssh:"):
+        raise ValueError(
+            "U2F application string does not start with b'ssh:' "
+            f"({application})"
+        )
+    return application, data
+
+
+class _SSHFormatSKEd25519:
+    """
+    The format of a sk-ssh-ed25519@openssh.com public key is:
+
+        string		"sk-ssh-ed25519@openssh.com"
+        string		public key
+        string		application (user-specified, but typically "ssh:")
+    """
+
+    def load_public(
+        self, data: memoryview
+    ) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
+        """Make Ed25519 public key from data."""
+        public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data)
+        _, data = load_application(data)
+        return public_key, data
+
+
+class _SSHFormatSKECDSA:
+    """
+    The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
+
+        string		"sk-ecdsa-sha2-nistp256@openssh.com"
+        string		curve name
+        ec_point	Q
+        string		application (user-specified, but typically "ssh:")
+    """
+
+    def load_public(
+        self, data: memoryview
+    ) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
+        """Make ECDSA public key from data."""
+        public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data)
+        _, data = load_application(data)
+        return public_key, data
+
+
+_KEY_FORMATS = {
+    _SSH_RSA: _SSHFormatRSA(),
+    _SSH_DSA: _SSHFormatDSA(),
+    _SSH_ED25519: _SSHFormatEd25519(),
+    _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
+    _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
+    _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
+    _SK_SSH_ED25519: _SSHFormatSKEd25519(),
+    _SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(),
+}
+
+
+def _lookup_kformat(key_type: bytes):
+    """Return valid format or throw error"""
+    if not isinstance(key_type, bytes):
+        key_type = memoryview(key_type).tobytes()
+    if key_type in _KEY_FORMATS:
+        return _KEY_FORMATS[key_type]
+    raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}")
+
+
+SSHPrivateKeyTypes = typing.Union[
+    ec.EllipticCurvePrivateKey,
+    rsa.RSAPrivateKey,
+    dsa.DSAPrivateKey,
+    ed25519.Ed25519PrivateKey,
+]
+
+
+def load_ssh_private_key(
+    data: bytes,
+    password: bytes | None,
+    backend: typing.Any = None,
+) -> SSHPrivateKeyTypes:
+    """Load private key from OpenSSH custom encoding."""
+    utils._check_byteslike("data", data)
+    if password is not None:
+        utils._check_bytes("password", password)
+
+    m = _PEM_RC.search(data)
+    if not m:
+        raise ValueError("Not OpenSSH private key format")
+    p1 = m.start(1)
+    p2 = m.end(1)
+    data = binascii.a2b_base64(memoryview(data)[p1:p2])
+    if not data.startswith(_SK_MAGIC):
+        raise ValueError("Not OpenSSH private key format")
+    data = memoryview(data)[len(_SK_MAGIC) :]
+
+    # parse header
+    ciphername, data = _get_sshstr(data)
+    kdfname, data = _get_sshstr(data)
+    kdfoptions, data = _get_sshstr(data)
+    nkeys, data = _get_u32(data)
+    if nkeys != 1:
+        raise ValueError("Only one key supported")
+
+    # load public key data
+    pubdata, data = _get_sshstr(data)
+    pub_key_type, pubdata = _get_sshstr(pubdata)
+    kformat = _lookup_kformat(pub_key_type)
+    pubfields, pubdata = kformat.get_public(pubdata)
+    _check_empty(pubdata)
+
+    if (ciphername, kdfname) != (_NONE, _NONE):
+        ciphername_bytes = ciphername.tobytes()
+        if ciphername_bytes not in _SSH_CIPHERS:
+            raise UnsupportedAlgorithm(
+                f"Unsupported cipher: {ciphername_bytes!r}"
+            )
+        if kdfname != _BCRYPT:
+            raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}")
+        blklen = _SSH_CIPHERS[ciphername_bytes].block_len
+        tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len
+        # load secret data
+        edata, data = _get_sshstr(data)
+        # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for
+        # information about how OpenSSH handles AEAD tags
+        if _SSH_CIPHERS[ciphername_bytes].is_aead:
+            tag = bytes(data)
+            if len(tag) != tag_len:
+                raise ValueError("Corrupt data: invalid tag length for cipher")
+        else:
+            _check_empty(data)
+        _check_block_size(edata, blklen)
+        salt, kbuf = _get_sshstr(kdfoptions)
+        rounds, kbuf = _get_u32(kbuf)
+        _check_empty(kbuf)
+        ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds)
+        dec = ciph.decryptor()
+        edata = memoryview(dec.update(edata))
+        if _SSH_CIPHERS[ciphername_bytes].is_aead:
+            assert isinstance(dec, AEADDecryptionContext)
+            _check_empty(dec.finalize_with_tag(tag))
+        else:
+            # _check_block_size requires data to be a full block so there
+            # should be no output from finalize
+            _check_empty(dec.finalize())
+    else:
+        # load secret data
+        edata, data = _get_sshstr(data)
+        _check_empty(data)
+        blklen = 8
+        _check_block_size(edata, blklen)
+    ck1, edata = _get_u32(edata)
+    ck2, edata = _get_u32(edata)
+    if ck1 != ck2:
+        raise ValueError("Corrupt data: broken checksum")
+
+    # load per-key struct
+    key_type, edata = _get_sshstr(edata)
+    if key_type != pub_key_type:
+        raise ValueError("Corrupt data: key type mismatch")
+    private_key, edata = kformat.load_private(edata, pubfields)
+    # We don't use the comment
+    _, edata = _get_sshstr(edata)
+
+    # yes, SSH does padding check *after* all other parsing is done.
+    # need to follow as it writes zero-byte padding too.
+    if edata != _PADDING[: len(edata)]:
+        raise ValueError("Corrupt data: invalid padding")
+
+    if isinstance(private_key, dsa.DSAPrivateKey):
+        warnings.warn(
+            "SSH DSA keys are deprecated and will be removed in a future "
+            "release.",
+            utils.DeprecatedIn40,
+            stacklevel=2,
+        )
+
+    return private_key
+
+
+def _serialize_ssh_private_key(
+    private_key: SSHPrivateKeyTypes,
+    password: bytes,
+    encryption_algorithm: KeySerializationEncryption,
+) -> bytes:
+    """Serialize private key with OpenSSH custom encoding."""
+    utils._check_bytes("password", password)
+    if isinstance(private_key, dsa.DSAPrivateKey):
+        warnings.warn(
+            "SSH DSA key support is deprecated and will be "
+            "removed in a future release",
+            utils.DeprecatedIn40,
+            stacklevel=4,
+        )
+
+    key_type = _get_ssh_key_type(private_key)
+    kformat = _lookup_kformat(key_type)
+
+    # setup parameters
+    f_kdfoptions = _FragList()
+    if password:
+        ciphername = _DEFAULT_CIPHER
+        blklen = _SSH_CIPHERS[ciphername].block_len
+        kdfname = _BCRYPT
+        rounds = _DEFAULT_ROUNDS
+        if (
+            isinstance(encryption_algorithm, _KeySerializationEncryption)
+            and encryption_algorithm._kdf_rounds is not None
+        ):
+            rounds = encryption_algorithm._kdf_rounds
+        salt = os.urandom(16)
+        f_kdfoptions.put_sshstr(salt)
+        f_kdfoptions.put_u32(rounds)
+        ciph = _init_cipher(ciphername, password, salt, rounds)
+    else:
+        ciphername = kdfname = _NONE
+        blklen = 8
+        ciph = None
+    nkeys = 1
+    checkval = os.urandom(4)
+    comment = b""
+
+    # encode public and private parts together
+    f_public_key = _FragList()
+    f_public_key.put_sshstr(key_type)
+    kformat.encode_public(private_key.public_key(), f_public_key)
+
+    f_secrets = _FragList([checkval, checkval])
+    f_secrets.put_sshstr(key_type)
+    kformat.encode_private(private_key, f_secrets)
+    f_secrets.put_sshstr(comment)
+    f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)])
+
+    # top-level structure
+    f_main = _FragList()
+    f_main.put_raw(_SK_MAGIC)
+    f_main.put_sshstr(ciphername)
+    f_main.put_sshstr(kdfname)
+    f_main.put_sshstr(f_kdfoptions)
+    f_main.put_u32(nkeys)
+    f_main.put_sshstr(f_public_key)
+    f_main.put_sshstr(f_secrets)
+
+    # copy result info bytearray
+    slen = f_secrets.size()
+    mlen = f_main.size()
+    buf = memoryview(bytearray(mlen + blklen))
+    f_main.render(buf)
+    ofs = mlen - slen
+
+    # encrypt in-place
+    if ciph is not None:
+        ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
+
+    return _ssh_pem_encode(buf[:mlen])
+
+
+SSHPublicKeyTypes = typing.Union[
+    ec.EllipticCurvePublicKey,
+    rsa.RSAPublicKey,
+    dsa.DSAPublicKey,
+    ed25519.Ed25519PublicKey,
+]
+
+SSHCertPublicKeyTypes = typing.Union[
+    ec.EllipticCurvePublicKey,
+    rsa.RSAPublicKey,
+    ed25519.Ed25519PublicKey,
+]
+
+
+class SSHCertificateType(enum.Enum):
+    USER = 1
+    HOST = 2
+
+
+class SSHCertificate:
+    def __init__(
+        self,
+        _nonce: memoryview,
+        _public_key: SSHPublicKeyTypes,
+        _serial: int,
+        _cctype: int,
+        _key_id: memoryview,
+        _valid_principals: list[bytes],
+        _valid_after: int,
+        _valid_before: int,
+        _critical_options: dict[bytes, bytes],
+        _extensions: dict[bytes, bytes],
+        _sig_type: memoryview,
+        _sig_key: memoryview,
+        _inner_sig_type: memoryview,
+        _signature: memoryview,
+        _tbs_cert_body: memoryview,
+        _cert_key_type: bytes,
+        _cert_body: memoryview,
+    ):
+        self._nonce = _nonce
+        self._public_key = _public_key
+        self._serial = _serial
+        try:
+            self._type = SSHCertificateType(_cctype)
+        except ValueError:
+            raise ValueError("Invalid certificate type")
+        self._key_id = _key_id
+        self._valid_principals = _valid_principals
+        self._valid_after = _valid_after
+        self._valid_before = _valid_before
+        self._critical_options = _critical_options
+        self._extensions = _extensions
+        self._sig_type = _sig_type
+        self._sig_key = _sig_key
+        self._inner_sig_type = _inner_sig_type
+        self._signature = _signature
+        self._cert_key_type = _cert_key_type
+        self._cert_body = _cert_body
+        self._tbs_cert_body = _tbs_cert_body
+
+    @property
+    def nonce(self) -> bytes:
+        return bytes(self._nonce)
+
+    def public_key(self) -> SSHCertPublicKeyTypes:
+        # make mypy happy until we remove DSA support entirely and
+        # the underlying union won't have a disallowed type
+        return typing.cast(SSHCertPublicKeyTypes, self._public_key)
+
+    @property
+    def serial(self) -> int:
+        return self._serial
+
+    @property
+    def type(self) -> SSHCertificateType:
+        return self._type
+
+    @property
+    def key_id(self) -> bytes:
+        return bytes(self._key_id)
+
+    @property
+    def valid_principals(self) -> list[bytes]:
+        return self._valid_principals
+
+    @property
+    def valid_before(self) -> int:
+        return self._valid_before
+
+    @property
+    def valid_after(self) -> int:
+        return self._valid_after
+
+    @property
+    def critical_options(self) -> dict[bytes, bytes]:
+        return self._critical_options
+
+    @property
+    def extensions(self) -> dict[bytes, bytes]:
+        return self._extensions
+
+    def signature_key(self) -> SSHCertPublicKeyTypes:
+        sigformat = _lookup_kformat(self._sig_type)
+        signature_key, sigkey_rest = sigformat.load_public(self._sig_key)
+        _check_empty(sigkey_rest)
+        return signature_key
+
+    def public_bytes(self) -> bytes:
+        return (
+            bytes(self._cert_key_type)
+            + b" "
+            + binascii.b2a_base64(bytes(self._cert_body), newline=False)
+        )
+
+    def verify_cert_signature(self) -> None:
+        signature_key = self.signature_key()
+        if isinstance(signature_key, ed25519.Ed25519PublicKey):
+            signature_key.verify(
+                bytes(self._signature), bytes(self._tbs_cert_body)
+            )
+        elif isinstance(signature_key, ec.EllipticCurvePublicKey):
+            # The signature is encoded as a pair of big-endian integers
+            r, data = _get_mpint(self._signature)
+            s, data = _get_mpint(data)
+            _check_empty(data)
+            computed_sig = asym_utils.encode_dss_signature(r, s)
+            hash_alg = _get_ec_hash_alg(signature_key.curve)
+            signature_key.verify(
+                computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg)
+            )
+        else:
+            assert isinstance(signature_key, rsa.RSAPublicKey)
+            if self._inner_sig_type == _SSH_RSA:
+                hash_alg = hashes.SHA1()
+            elif self._inner_sig_type == _SSH_RSA_SHA256:
+                hash_alg = hashes.SHA256()
+            else:
+                assert self._inner_sig_type == _SSH_RSA_SHA512
+                hash_alg = hashes.SHA512()
+            signature_key.verify(
+                bytes(self._signature),
+                bytes(self._tbs_cert_body),
+                padding.PKCS1v15(),
+                hash_alg,
+            )
+
+
+def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm:
+    if isinstance(curve, ec.SECP256R1):
+        return hashes.SHA256()
+    elif isinstance(curve, ec.SECP384R1):
+        return hashes.SHA384()
+    else:
+        assert isinstance(curve, ec.SECP521R1)
+        return hashes.SHA512()
+
+
+def _load_ssh_public_identity(
+    data: bytes,
+    _legacy_dsa_allowed=False,
+) -> SSHCertificate | SSHPublicKeyTypes:
+    utils._check_byteslike("data", data)
+
+    m = _SSH_PUBKEY_RC.match(data)
+    if not m:
+        raise ValueError("Invalid line format")
+    key_type = orig_key_type = m.group(1)
+    key_body = m.group(2)
+    with_cert = False
+    if key_type.endswith(_CERT_SUFFIX):
+        with_cert = True
+        key_type = key_type[: -len(_CERT_SUFFIX)]
+    if key_type == _SSH_DSA and not _legacy_dsa_allowed:
+        raise UnsupportedAlgorithm(
+            "DSA keys aren't supported in SSH certificates"
+        )
+    kformat = _lookup_kformat(key_type)
+
+    try:
+        rest = memoryview(binascii.a2b_base64(key_body))
+    except (TypeError, binascii.Error):
+        raise ValueError("Invalid format")
+
+    if with_cert:
+        cert_body = rest
+    inner_key_type, rest = _get_sshstr(rest)
+    if inner_key_type != orig_key_type:
+        raise ValueError("Invalid key format")
+    if with_cert:
+        nonce, rest = _get_sshstr(rest)
+    public_key, rest = kformat.load_public(rest)
+    if with_cert:
+        serial, rest = _get_u64(rest)
+        cctype, rest = _get_u32(rest)
+        key_id, rest = _get_sshstr(rest)
+        principals, rest = _get_sshstr(rest)
+        valid_principals = []
+        while principals:
+            principal, principals = _get_sshstr(principals)
+            valid_principals.append(bytes(principal))
+        valid_after, rest = _get_u64(rest)
+        valid_before, rest = _get_u64(rest)
+        crit_options, rest = _get_sshstr(rest)
+        critical_options = _parse_exts_opts(crit_options)
+        exts, rest = _get_sshstr(rest)
+        extensions = _parse_exts_opts(exts)
+        # Get the reserved field, which is unused.
+        _, rest = _get_sshstr(rest)
+        sig_key_raw, rest = _get_sshstr(rest)
+        sig_type, sig_key = _get_sshstr(sig_key_raw)
+        if sig_type == _SSH_DSA and not _legacy_dsa_allowed:
+            raise UnsupportedAlgorithm(
+                "DSA signatures aren't supported in SSH certificates"
+            )
+        # Get the entire cert body and subtract the signature
+        tbs_cert_body = cert_body[: -len(rest)]
+        signature_raw, rest = _get_sshstr(rest)
+        _check_empty(rest)
+        inner_sig_type, sig_rest = _get_sshstr(signature_raw)
+        # RSA certs can have multiple algorithm types
+        if (
+            sig_type == _SSH_RSA
+            and inner_sig_type
+            not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA]
+        ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type):
+            raise ValueError("Signature key type does not match")
+        signature, sig_rest = _get_sshstr(sig_rest)
+        _check_empty(sig_rest)
+        return SSHCertificate(
+            nonce,
+            public_key,
+            serial,
+            cctype,
+            key_id,
+            valid_principals,
+            valid_after,
+            valid_before,
+            critical_options,
+            extensions,
+            sig_type,
+            sig_key,
+            inner_sig_type,
+            signature,
+            tbs_cert_body,
+            orig_key_type,
+            cert_body,
+        )
+    else:
+        _check_empty(rest)
+        return public_key
+
+
+def load_ssh_public_identity(
+    data: bytes,
+) -> SSHCertificate | SSHPublicKeyTypes:
+    return _load_ssh_public_identity(data)
+
+
+def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]:
+    result: dict[bytes, bytes] = {}
+    last_name = None
+    while exts_opts:
+        name, exts_opts = _get_sshstr(exts_opts)
+        bname: bytes = bytes(name)
+        if bname in result:
+            raise ValueError("Duplicate name")
+        if last_name is not None and bname < last_name:
+            raise ValueError("Fields not lexically sorted")
+        value, exts_opts = _get_sshstr(exts_opts)
+        if len(value) > 0:
+            value, extra = _get_sshstr(value)
+            if len(extra) > 0:
+                raise ValueError("Unexpected extra data after value")
+        result[bname] = bytes(value)
+        last_name = bname
+    return result
+
+
+def load_ssh_public_key(
+    data: bytes, backend: typing.Any = None
+) -> SSHPublicKeyTypes:
+    cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True)
+    public_key: SSHPublicKeyTypes
+    if isinstance(cert_or_key, SSHCertificate):
+        public_key = cert_or_key.public_key()
+    else:
+        public_key = cert_or_key
+
+    if isinstance(public_key, dsa.DSAPublicKey):
+        warnings.warn(
+            "SSH DSA keys are deprecated and will be removed in a future "
+            "release.",
+            utils.DeprecatedIn40,
+            stacklevel=2,
+        )
+    return public_key
+
+
+def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes:
+    """One-line public key format for OpenSSH"""
+    if isinstance(public_key, dsa.DSAPublicKey):
+        warnings.warn(
+            "SSH DSA key support is deprecated and will be "
+            "removed in a future release",
+            utils.DeprecatedIn40,
+            stacklevel=4,
+        )
+    key_type = _get_ssh_key_type(public_key)
+    kformat = _lookup_kformat(key_type)
+
+    f_pub = _FragList()
+    f_pub.put_sshstr(key_type)
+    kformat.encode_public(public_key, f_pub)
+
+    pub = binascii.b2a_base64(f_pub.tobytes()).strip()
+    return b"".join([key_type, b" ", pub])
+
+
+SSHCertPrivateKeyTypes = typing.Union[
+    ec.EllipticCurvePrivateKey,
+    rsa.RSAPrivateKey,
+    ed25519.Ed25519PrivateKey,
+]
+
+
+# This is an undocumented limit enforced in the openssh codebase for sshd and
+# ssh-keygen, but it is undefined in the ssh certificates spec.
+_SSHKEY_CERT_MAX_PRINCIPALS = 256
+
+
+class SSHCertificateBuilder:
+    def __init__(
+        self,
+        _public_key: SSHCertPublicKeyTypes | None = None,
+        _serial: int | None = None,
+        _type: SSHCertificateType | None = None,
+        _key_id: bytes | None = None,
+        _valid_principals: list[bytes] = [],
+        _valid_for_all_principals: bool = False,
+        _valid_before: int | None = None,
+        _valid_after: int | None = None,
+        _critical_options: list[tuple[bytes, bytes]] = [],
+        _extensions: list[tuple[bytes, bytes]] = [],
+    ):
+        self._public_key = _public_key
+        self._serial = _serial
+        self._type = _type
+        self._key_id = _key_id
+        self._valid_principals = _valid_principals
+        self._valid_for_all_principals = _valid_for_all_principals
+        self._valid_before = _valid_before
+        self._valid_after = _valid_after
+        self._critical_options = _critical_options
+        self._extensions = _extensions
+
+    def public_key(
+        self, public_key: SSHCertPublicKeyTypes
+    ) -> SSHCertificateBuilder:
+        if not isinstance(
+            public_key,
+            (
+                ec.EllipticCurvePublicKey,
+                rsa.RSAPublicKey,
+                ed25519.Ed25519PublicKey,
+            ),
+        ):
+            raise TypeError("Unsupported key type")
+        if self._public_key is not None:
+            raise ValueError("public_key already set")
+
+        return SSHCertificateBuilder(
+            _public_key=public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def serial(self, serial: int) -> SSHCertificateBuilder:
+        if not isinstance(serial, int):
+            raise TypeError("serial must be an integer")
+        if not 0 <= serial < 2**64:
+            raise ValueError("serial must be between 0 and 2**64")
+        if self._serial is not None:
+            raise ValueError("serial already set")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def type(self, type: SSHCertificateType) -> SSHCertificateBuilder:
+        if not isinstance(type, SSHCertificateType):
+            raise TypeError("type must be an SSHCertificateType")
+        if self._type is not None:
+            raise ValueError("type already set")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def key_id(self, key_id: bytes) -> SSHCertificateBuilder:
+        if not isinstance(key_id, bytes):
+            raise TypeError("key_id must be bytes")
+        if self._key_id is not None:
+            raise ValueError("key_id already set")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def valid_principals(
+        self, valid_principals: list[bytes]
+    ) -> SSHCertificateBuilder:
+        if self._valid_for_all_principals:
+            raise ValueError(
+                "Principals can't be set because the cert is valid "
+                "for all principals"
+            )
+        if (
+            not all(isinstance(x, bytes) for x in valid_principals)
+            or not valid_principals
+        ):
+            raise TypeError(
+                "principals must be a list of bytes and can't be empty"
+            )
+        if self._valid_principals:
+            raise ValueError("valid_principals already set")
+
+        if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS:
+            raise ValueError(
+                "Reached or exceeded the maximum number of valid_principals"
+            )
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def valid_for_all_principals(self):
+        if self._valid_principals:
+            raise ValueError(
+                "valid_principals already set, can't set "
+                "valid_for_all_principals"
+            )
+        if self._valid_for_all_principals:
+            raise ValueError("valid_for_all_principals already set")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=True,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder:
+        if not isinstance(valid_before, (int, float)):
+            raise TypeError("valid_before must be an int or float")
+        valid_before = int(valid_before)
+        if valid_before < 0 or valid_before >= 2**64:
+            raise ValueError("valid_before must [0, 2**64)")
+        if self._valid_before is not None:
+            raise ValueError("valid_before already set")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder:
+        if not isinstance(valid_after, (int, float)):
+            raise TypeError("valid_after must be an int or float")
+        valid_after = int(valid_after)
+        if valid_after < 0 or valid_after >= 2**64:
+            raise ValueError("valid_after must [0, 2**64)")
+        if self._valid_after is not None:
+            raise ValueError("valid_after already set")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=valid_after,
+            _critical_options=self._critical_options,
+            _extensions=self._extensions,
+        )
+
+    def add_critical_option(
+        self, name: bytes, value: bytes
+    ) -> SSHCertificateBuilder:
+        if not isinstance(name, bytes) or not isinstance(value, bytes):
+            raise TypeError("name and value must be bytes")
+        # This is O(n**2)
+        if name in [name for name, _ in self._critical_options]:
+            raise ValueError("Duplicate critical option name")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=[*self._critical_options, (name, value)],
+            _extensions=self._extensions,
+        )
+
+    def add_extension(
+        self, name: bytes, value: bytes
+    ) -> SSHCertificateBuilder:
+        if not isinstance(name, bytes) or not isinstance(value, bytes):
+            raise TypeError("name and value must be bytes")
+        # This is O(n**2)
+        if name in [name for name, _ in self._extensions]:
+            raise ValueError("Duplicate extension name")
+
+        return SSHCertificateBuilder(
+            _public_key=self._public_key,
+            _serial=self._serial,
+            _type=self._type,
+            _key_id=self._key_id,
+            _valid_principals=self._valid_principals,
+            _valid_for_all_principals=self._valid_for_all_principals,
+            _valid_before=self._valid_before,
+            _valid_after=self._valid_after,
+            _critical_options=self._critical_options,
+            _extensions=[*self._extensions, (name, value)],
+        )
+
+    def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate:
+        if not isinstance(
+            private_key,
+            (
+                ec.EllipticCurvePrivateKey,
+                rsa.RSAPrivateKey,
+                ed25519.Ed25519PrivateKey,
+            ),
+        ):
+            raise TypeError("Unsupported private key type")
+
+        if self._public_key is None:
+            raise ValueError("public_key must be set")
+
+        # Not required
+        serial = 0 if self._serial is None else self._serial
+
+        if self._type is None:
+            raise ValueError("type must be set")
+
+        # Not required
+        key_id = b"" if self._key_id is None else self._key_id
+
+        # A zero length list is valid, but means the certificate
+        # is valid for any principal of the specified type. We require
+        # the user to explicitly set valid_for_all_principals to get
+        # that behavior.
+        if not self._valid_principals and not self._valid_for_all_principals:
+            raise ValueError(
+                "valid_principals must be set if valid_for_all_principals "
+                "is False"
+            )
+
+        if self._valid_before is None:
+            raise ValueError("valid_before must be set")
+
+        if self._valid_after is None:
+            raise ValueError("valid_after must be set")
+
+        if self._valid_after > self._valid_before:
+            raise ValueError("valid_after must be earlier than valid_before")
+
+        # lexically sort our byte strings
+        self._critical_options.sort(key=lambda x: x[0])
+        self._extensions.sort(key=lambda x: x[0])
+
+        key_type = _get_ssh_key_type(self._public_key)
+        cert_prefix = key_type + _CERT_SUFFIX
+
+        # Marshal the bytes to be signed
+        nonce = os.urandom(32)
+        kformat = _lookup_kformat(key_type)
+        f = _FragList()
+        f.put_sshstr(cert_prefix)
+        f.put_sshstr(nonce)
+        kformat.encode_public(self._public_key, f)
+        f.put_u64(serial)
+        f.put_u32(self._type.value)
+        f.put_sshstr(key_id)
+        fprincipals = _FragList()
+        for p in self._valid_principals:
+            fprincipals.put_sshstr(p)
+        f.put_sshstr(fprincipals.tobytes())
+        f.put_u64(self._valid_after)
+        f.put_u64(self._valid_before)
+        fcrit = _FragList()
+        for name, value in self._critical_options:
+            fcrit.put_sshstr(name)
+            if len(value) > 0:
+                foptval = _FragList()
+                foptval.put_sshstr(value)
+                fcrit.put_sshstr(foptval.tobytes())
+            else:
+                fcrit.put_sshstr(value)
+        f.put_sshstr(fcrit.tobytes())
+        fext = _FragList()
+        for name, value in self._extensions:
+            fext.put_sshstr(name)
+            if len(value) > 0:
+                fextval = _FragList()
+                fextval.put_sshstr(value)
+                fext.put_sshstr(fextval.tobytes())
+            else:
+                fext.put_sshstr(value)
+        f.put_sshstr(fext.tobytes())
+        f.put_sshstr(b"")  # RESERVED FIELD
+        # encode CA public key
+        ca_type = _get_ssh_key_type(private_key)
+        caformat = _lookup_kformat(ca_type)
+        caf = _FragList()
+        caf.put_sshstr(ca_type)
+        caformat.encode_public(private_key.public_key(), caf)
+        f.put_sshstr(caf.tobytes())
+        # Sigs according to the rules defined for the CA's public key
+        # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA,
+        # and RFC8032 for Ed25519).
+        if isinstance(private_key, ed25519.Ed25519PrivateKey):
+            signature = private_key.sign(f.tobytes())
+            fsig = _FragList()
+            fsig.put_sshstr(ca_type)
+            fsig.put_sshstr(signature)
+            f.put_sshstr(fsig.tobytes())
+        elif isinstance(private_key, ec.EllipticCurvePrivateKey):
+            hash_alg = _get_ec_hash_alg(private_key.curve)
+            signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg))
+            r, s = asym_utils.decode_dss_signature(signature)
+            fsig = _FragList()
+            fsig.put_sshstr(ca_type)
+            fsigblob = _FragList()
+            fsigblob.put_mpint(r)
+            fsigblob.put_mpint(s)
+            fsig.put_sshstr(fsigblob.tobytes())
+            f.put_sshstr(fsig.tobytes())
+
+        else:
+            assert isinstance(private_key, rsa.RSAPrivateKey)
+            # Just like Golang, we're going to use SHA512 for RSA
+            # https://cs.opensource.google/go/x/crypto/+/refs/tags/
+            # v0.4.0:ssh/certs.go;l=445
+            # RFC 8332 defines SHA256 and 512 as options
+            fsig = _FragList()
+            fsig.put_sshstr(_SSH_RSA_SHA512)
+            signature = private_key.sign(
+                f.tobytes(), padding.PKCS1v15(), hashes.SHA512()
+            )
+            fsig.put_sshstr(signature)
+            f.put_sshstr(fsig.tobytes())
+
+        cert_data = binascii.b2a_base64(f.tobytes()).strip()
+        # load_ssh_public_identity returns a union, but this is
+        # guaranteed to be an SSHCertificate, so we cast to make
+        # mypy happy.
+        return typing.cast(
+            SSHCertificate,
+            load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])),
+        )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1af423004863e7db3e163f61a6baa4ddc157351
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py
@@ -0,0 +1,9 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+
+class InvalidToken(Exception):
+    pass
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py
new file mode 100644
index 0000000000000000000000000000000000000000..af5ab6efe290099ef7c83cd6f6897744fb9a42f2
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -0,0 +1,92 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import base64
+import typing
+from urllib.parse import quote, urlencode
+
+from cryptography.hazmat.primitives import constant_time, hmac
+from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
+from cryptography.hazmat.primitives.twofactor import InvalidToken
+
+HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512]
+
+
+def _generate_uri(
+    hotp: HOTP,
+    type_name: str,
+    account_name: str,
+    issuer: str | None,
+    extra_parameters: list[tuple[str, int]],
+) -> str:
+    parameters = [
+        ("digits", hotp._length),
+        ("secret", base64.b32encode(hotp._key)),
+        ("algorithm", hotp._algorithm.name.upper()),
+    ]
+
+    if issuer is not None:
+        parameters.append(("issuer", issuer))
+
+    parameters.extend(extra_parameters)
+
+    label = (
+        f"{quote(issuer)}:{quote(account_name)}"
+        if issuer
+        else quote(account_name)
+    )
+    return f"otpauth://{type_name}/{label}?{urlencode(parameters)}"
+
+
+class HOTP:
+    def __init__(
+        self,
+        key: bytes,
+        length: int,
+        algorithm: HOTPHashTypes,
+        backend: typing.Any = None,
+        enforce_key_length: bool = True,
+    ) -> None:
+        if len(key) < 16 and enforce_key_length is True:
+            raise ValueError("Key length has to be at least 128 bits.")
+
+        if not isinstance(length, int):
+            raise TypeError("Length parameter must be an integer type.")
+
+        if length < 6 or length > 8:
+            raise ValueError("Length of HOTP has to be between 6 and 8.")
+
+        if not isinstance(algorithm, (SHA1, SHA256, SHA512)):
+            raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.")
+
+        self._key = key
+        self._length = length
+        self._algorithm = algorithm
+
+    def generate(self, counter: int) -> bytes:
+        truncated_value = self._dynamic_truncate(counter)
+        hotp = truncated_value % (10**self._length)
+        return "{0:0{1}}".format(hotp, self._length).encode()
+
+    def verify(self, hotp: bytes, counter: int) -> None:
+        if not constant_time.bytes_eq(self.generate(counter), hotp):
+            raise InvalidToken("Supplied HOTP value does not match.")
+
+    def _dynamic_truncate(self, counter: int) -> int:
+        ctx = hmac.HMAC(self._key, self._algorithm)
+        ctx.update(counter.to_bytes(length=8, byteorder="big"))
+        hmac_value = ctx.finalize()
+
+        offset = hmac_value[len(hmac_value) - 1] & 0b1111
+        p = hmac_value[offset : offset + 4]
+        return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF
+
+    def get_provisioning_uri(
+        self, account_name: str, counter: int, issuer: str | None
+    ) -> str:
+        return _generate_uri(
+            self, "hotp", account_name, issuer, [("counter", int(counter))]
+        )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/totp.py
new file mode 100644
index 0000000000000000000000000000000000000000..68a5077468e39588c5c4c975bedca428d85c70da
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/hazmat/primitives/twofactor/totp.py
@@ -0,0 +1,50 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography.hazmat.primitives import constant_time
+from cryptography.hazmat.primitives.twofactor import InvalidToken
+from cryptography.hazmat.primitives.twofactor.hotp import (
+    HOTP,
+    HOTPHashTypes,
+    _generate_uri,
+)
+
+
+class TOTP:
+    def __init__(
+        self,
+        key: bytes,
+        length: int,
+        algorithm: HOTPHashTypes,
+        time_step: int,
+        backend: typing.Any = None,
+        enforce_key_length: bool = True,
+    ):
+        self._time_step = time_step
+        self._hotp = HOTP(
+            key, length, algorithm, enforce_key_length=enforce_key_length
+        )
+
+    def generate(self, time: int | float) -> bytes:
+        counter = int(time / self._time_step)
+        return self._hotp.generate(counter)
+
+    def verify(self, totp: bytes, time: int) -> None:
+        if not constant_time.bytes_eq(self.generate(time), totp):
+            raise InvalidToken("Supplied TOTP value does not match.")
+
+    def get_provisioning_uri(
+        self, account_name: str, issuer: str | None
+    ) -> str:
+        return _generate_uri(
+            self._hotp,
+            "totp",
+            account_name,
+            issuer,
+            [("period", int(self._time_step))],
+        )
diff --git a/.venv/lib/python3.8/site-packages/cryptography/py.typed b/.venv/lib/python3.8/site-packages/cryptography/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/.venv/lib/python3.8/site-packages/cryptography/utils.py b/.venv/lib/python3.8/site-packages/cryptography/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..706d0ae4cbd72c83bf3445fc69e4a2178234dcbc
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/utils.py
@@ -0,0 +1,127 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import enum
+import sys
+import types
+import typing
+import warnings
+
+
+# We use a UserWarning subclass, instead of DeprecationWarning, because CPython
+# decided deprecation warnings should be invisible by default.
+class CryptographyDeprecationWarning(UserWarning):
+    pass
+
+
+# Several APIs were deprecated with no specific end-of-life date because of the
+# ubiquity of their use. They should not be removed until we agree on when that
+# cycle ends.
+DeprecatedIn36 = CryptographyDeprecationWarning
+DeprecatedIn37 = CryptographyDeprecationWarning
+DeprecatedIn40 = CryptographyDeprecationWarning
+DeprecatedIn41 = CryptographyDeprecationWarning
+DeprecatedIn42 = CryptographyDeprecationWarning
+DeprecatedIn43 = CryptographyDeprecationWarning
+
+
+def _check_bytes(name: str, value: bytes) -> None:
+    if not isinstance(value, bytes):
+        raise TypeError(f"{name} must be bytes")
+
+
+def _check_byteslike(name: str, value: bytes) -> None:
+    try:
+        memoryview(value)
+    except TypeError:
+        raise TypeError(f"{name} must be bytes-like")
+
+
+def int_to_bytes(integer: int, length: int | None = None) -> bytes:
+    if length == 0:
+        raise ValueError("length argument can't be 0")
+    return integer.to_bytes(
+        length or (integer.bit_length() + 7) // 8 or 1, "big"
+    )
+
+
+class InterfaceNotImplemented(Exception):
+    pass
+
+
+class _DeprecatedValue:
+    def __init__(self, value: object, message: str, warning_class):
+        self.value = value
+        self.message = message
+        self.warning_class = warning_class
+
+
+class _ModuleWithDeprecations(types.ModuleType):
+    def __init__(self, module: types.ModuleType):
+        super().__init__(module.__name__)
+        self.__dict__["_module"] = module
+
+    def __getattr__(self, attr: str) -> object:
+        obj = getattr(self._module, attr)
+        if isinstance(obj, _DeprecatedValue):
+            warnings.warn(obj.message, obj.warning_class, stacklevel=2)
+            obj = obj.value
+        return obj
+
+    def __setattr__(self, attr: str, value: object) -> None:
+        setattr(self._module, attr, value)
+
+    def __delattr__(self, attr: str) -> None:
+        obj = getattr(self._module, attr)
+        if isinstance(obj, _DeprecatedValue):
+            warnings.warn(obj.message, obj.warning_class, stacklevel=2)
+
+        delattr(self._module, attr)
+
+    def __dir__(self) -> typing.Sequence[str]:
+        return ["_module", *dir(self._module)]
+
+
+def deprecated(
+    value: object,
+    module_name: str,
+    message: str,
+    warning_class: type[Warning],
+    name: str | None = None,
+) -> _DeprecatedValue:
+    module = sys.modules[module_name]
+    if not isinstance(module, _ModuleWithDeprecations):
+        sys.modules[module_name] = module = _ModuleWithDeprecations(module)
+    dv = _DeprecatedValue(value, message, warning_class)
+    # Maintain backwards compatibility with `name is None` for pyOpenSSL.
+    if name is not None:
+        setattr(module, name, dv)
+    return dv
+
+
+def cached_property(func: typing.Callable) -> property:
+    cached_name = f"_cached_{func}"
+    sentinel = object()
+
+    def inner(instance: object):
+        cache = getattr(instance, cached_name, sentinel)
+        if cache is not sentinel:
+            return cache
+        result = func(instance)
+        setattr(instance, cached_name, result)
+        return result
+
+    return property(inner)
+
+
+# Python 3.10 changed representation of enums. We use well-defined object
+# representation and string representation from Python 3.9.
+class Enum(enum.Enum):
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>"
+
+    def __str__(self) -> str:
+        return f"{self.__class__.__name__}.{self._name_}"
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/__init__.py b/.venv/lib/python3.8/site-packages/cryptography/x509/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..26c6444c511f764e1e070b4a6cde5fe026f9187a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/__init__.py
@@ -0,0 +1,259 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.x509 import certificate_transparency, verification
+from cryptography.x509.base import (
+    Attribute,
+    AttributeNotFound,
+    Attributes,
+    Certificate,
+    CertificateBuilder,
+    CertificateRevocationList,
+    CertificateRevocationListBuilder,
+    CertificateSigningRequest,
+    CertificateSigningRequestBuilder,
+    InvalidVersion,
+    RevokedCertificate,
+    RevokedCertificateBuilder,
+    Version,
+    load_der_x509_certificate,
+    load_der_x509_crl,
+    load_der_x509_csr,
+    load_pem_x509_certificate,
+    load_pem_x509_certificates,
+    load_pem_x509_crl,
+    load_pem_x509_csr,
+    random_serial_number,
+)
+from cryptography.x509.extensions import (
+    AccessDescription,
+    AuthorityInformationAccess,
+    AuthorityKeyIdentifier,
+    BasicConstraints,
+    CertificateIssuer,
+    CertificatePolicies,
+    CRLDistributionPoints,
+    CRLNumber,
+    CRLReason,
+    DeltaCRLIndicator,
+    DistributionPoint,
+    DuplicateExtension,
+    ExtendedKeyUsage,
+    Extension,
+    ExtensionNotFound,
+    Extensions,
+    ExtensionType,
+    FreshestCRL,
+    GeneralNames,
+    InhibitAnyPolicy,
+    InvalidityDate,
+    IssuerAlternativeName,
+    IssuingDistributionPoint,
+    KeyUsage,
+    MSCertificateTemplate,
+    NameConstraints,
+    NoticeReference,
+    OCSPAcceptableResponses,
+    OCSPNoCheck,
+    OCSPNonce,
+    PolicyConstraints,
+    PolicyInformation,
+    PrecertificateSignedCertificateTimestamps,
+    PrecertPoison,
+    ReasonFlags,
+    SignedCertificateTimestamps,
+    SubjectAlternativeName,
+    SubjectInformationAccess,
+    SubjectKeyIdentifier,
+    TLSFeature,
+    TLSFeatureType,
+    UnrecognizedExtension,
+    UserNotice,
+)
+from cryptography.x509.general_name import (
+    DirectoryName,
+    DNSName,
+    GeneralName,
+    IPAddress,
+    OtherName,
+    RegisteredID,
+    RFC822Name,
+    UniformResourceIdentifier,
+    UnsupportedGeneralNameType,
+)
+from cryptography.x509.name import (
+    Name,
+    NameAttribute,
+    RelativeDistinguishedName,
+)
+from cryptography.x509.oid import (
+    AuthorityInformationAccessOID,
+    CertificatePoliciesOID,
+    CRLEntryExtensionOID,
+    ExtendedKeyUsageOID,
+    ExtensionOID,
+    NameOID,
+    ObjectIdentifier,
+    PublicKeyAlgorithmOID,
+    SignatureAlgorithmOID,
+)
+
+OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS
+OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER
+OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS
+OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES
+OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS
+OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE
+OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL
+OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY
+OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME
+OID_KEY_USAGE = ExtensionOID.KEY_USAGE
+OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS
+OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK
+OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS
+OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS
+OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME
+OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES
+OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS
+OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER
+
+OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1
+OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224
+OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256
+OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1
+OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224
+OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256
+OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384
+OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512
+OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5
+OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1
+OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224
+OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256
+OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384
+OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512
+OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS
+
+OID_COMMON_NAME = NameOID.COMMON_NAME
+OID_COUNTRY_NAME = NameOID.COUNTRY_NAME
+OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT
+OID_DN_QUALIFIER = NameOID.DN_QUALIFIER
+OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS
+OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER
+OID_GIVEN_NAME = NameOID.GIVEN_NAME
+OID_LOCALITY_NAME = NameOID.LOCALITY_NAME
+OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME
+OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME
+OID_PSEUDONYM = NameOID.PSEUDONYM
+OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER
+OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME
+OID_SURNAME = NameOID.SURNAME
+OID_TITLE = NameOID.TITLE
+
+OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH
+OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING
+OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION
+OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING
+OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH
+OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING
+
+OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY
+OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER
+OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE
+
+OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER
+OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON
+OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE
+
+OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS
+OID_OCSP = AuthorityInformationAccessOID.OCSP
+
+__all__ = [
+    "OID_CA_ISSUERS",
+    "OID_OCSP",
+    "AccessDescription",
+    "Attribute",
+    "AttributeNotFound",
+    "Attributes",
+    "AuthorityInformationAccess",
+    "AuthorityKeyIdentifier",
+    "BasicConstraints",
+    "CRLDistributionPoints",
+    "CRLNumber",
+    "CRLReason",
+    "Certificate",
+    "CertificateBuilder",
+    "CertificateIssuer",
+    "CertificatePolicies",
+    "CertificateRevocationList",
+    "CertificateRevocationListBuilder",
+    "CertificateSigningRequest",
+    "CertificateSigningRequestBuilder",
+    "DNSName",
+    "DeltaCRLIndicator",
+    "DirectoryName",
+    "DistributionPoint",
+    "DuplicateExtension",
+    "ExtendedKeyUsage",
+    "Extension",
+    "ExtensionNotFound",
+    "ExtensionType",
+    "Extensions",
+    "FreshestCRL",
+    "GeneralName",
+    "GeneralNames",
+    "IPAddress",
+    "InhibitAnyPolicy",
+    "InvalidVersion",
+    "InvalidityDate",
+    "IssuerAlternativeName",
+    "IssuingDistributionPoint",
+    "KeyUsage",
+    "MSCertificateTemplate",
+    "Name",
+    "NameAttribute",
+    "NameConstraints",
+    "NameOID",
+    "NoticeReference",
+    "OCSPAcceptableResponses",
+    "OCSPNoCheck",
+    "OCSPNonce",
+    "ObjectIdentifier",
+    "OtherName",
+    "PolicyConstraints",
+    "PolicyInformation",
+    "PrecertPoison",
+    "PrecertificateSignedCertificateTimestamps",
+    "PublicKeyAlgorithmOID",
+    "RFC822Name",
+    "ReasonFlags",
+    "RegisteredID",
+    "RelativeDistinguishedName",
+    "RevokedCertificate",
+    "RevokedCertificateBuilder",
+    "SignatureAlgorithmOID",
+    "SignedCertificateTimestamps",
+    "SubjectAlternativeName",
+    "SubjectInformationAccess",
+    "SubjectKeyIdentifier",
+    "TLSFeature",
+    "TLSFeatureType",
+    "UniformResourceIdentifier",
+    "UnrecognizedExtension",
+    "UnsupportedGeneralNameType",
+    "UserNotice",
+    "Version",
+    "certificate_transparency",
+    "load_der_x509_certificate",
+    "load_der_x509_crl",
+    "load_der_x509_csr",
+    "load_pem_x509_certificate",
+    "load_pem_x509_certificates",
+    "load_pem_x509_crl",
+    "load_pem_x509_csr",
+    "random_serial_number",
+    "verification",
+    "verification",
+]
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/base.py b/.venv/lib/python3.8/site-packages/cryptography/x509/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ed41e6694c6237b7500c0d838a19826ad0e0dba
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/base.py
@@ -0,0 +1,1226 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import datetime
+import os
+import typing
+import warnings
+
+from cryptography import utils
+from cryptography.hazmat.bindings._rust import x509 as rust_x509
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import (
+    dsa,
+    ec,
+    ed448,
+    ed25519,
+    padding,
+    rsa,
+    x448,
+    x25519,
+)
+from cryptography.hazmat.primitives.asymmetric.types import (
+    CertificateIssuerPrivateKeyTypes,
+    CertificateIssuerPublicKeyTypes,
+    CertificatePublicKeyTypes,
+)
+from cryptography.x509.extensions import (
+    Extension,
+    Extensions,
+    ExtensionType,
+    _make_sequence_methods,
+)
+from cryptography.x509.name import Name, _ASN1Type
+from cryptography.x509.oid import ObjectIdentifier
+
+_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1)
+
+# This must be kept in sync with sign.rs's list of allowable types in
+# identify_hash_type
+_AllowedHashTypes = typing.Union[
+    hashes.SHA224,
+    hashes.SHA256,
+    hashes.SHA384,
+    hashes.SHA512,
+    hashes.SHA3_224,
+    hashes.SHA3_256,
+    hashes.SHA3_384,
+    hashes.SHA3_512,
+]
+
+
+class AttributeNotFound(Exception):
+    def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
+        super().__init__(msg)
+        self.oid = oid
+
+
+def _reject_duplicate_extension(
+    extension: Extension[ExtensionType],
+    extensions: list[Extension[ExtensionType]],
+) -> None:
+    # This is quadratic in the number of extensions
+    for e in extensions:
+        if e.oid == extension.oid:
+            raise ValueError("This extension has already been set.")
+
+
+def _reject_duplicate_attribute(
+    oid: ObjectIdentifier,
+    attributes: list[tuple[ObjectIdentifier, bytes, int | None]],
+) -> None:
+    # This is quadratic in the number of attributes
+    for attr_oid, _, _ in attributes:
+        if attr_oid == oid:
+            raise ValueError("This attribute has already been set.")
+
+
+def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime:
+    """Normalizes a datetime to a naive datetime in UTC.
+
+    time -- datetime to normalize. Assumed to be in UTC if not timezone
+            aware.
+    """
+    if time.tzinfo is not None:
+        offset = time.utcoffset()
+        offset = offset if offset else datetime.timedelta()
+        return time.replace(tzinfo=None) - offset
+    else:
+        return time
+
+
+class Attribute:
+    def __init__(
+        self,
+        oid: ObjectIdentifier,
+        value: bytes,
+        _type: int = _ASN1Type.UTF8String.value,
+    ) -> None:
+        self._oid = oid
+        self._value = value
+        self._type = _type
+
+    @property
+    def oid(self) -> ObjectIdentifier:
+        return self._oid
+
+    @property
+    def value(self) -> bytes:
+        return self._value
+
+    def __repr__(self) -> str:
+        return f"<Attribute(oid={self.oid}, value={self.value!r})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Attribute):
+            return NotImplemented
+
+        return (
+            self.oid == other.oid
+            and self.value == other.value
+            and self._type == other._type
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.oid, self.value, self._type))
+
+
+class Attributes:
+    def __init__(
+        self,
+        attributes: typing.Iterable[Attribute],
+    ) -> None:
+        self._attributes = list(attributes)
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes")
+
+    def __repr__(self) -> str:
+        return f"<Attributes({self._attributes})>"
+
+    def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute:
+        for attr in self:
+            if attr.oid == oid:
+                return attr
+
+        raise AttributeNotFound(f"No {oid} attribute was found", oid)
+
+
+class Version(utils.Enum):
+    v1 = 0
+    v3 = 2
+
+
+class InvalidVersion(Exception):
+    def __init__(self, msg: str, parsed_version: int) -> None:
+        super().__init__(msg)
+        self.parsed_version = parsed_version
+
+
+class Certificate(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes:
+        """
+        Returns bytes using digest passed.
+        """
+
+    @property
+    @abc.abstractmethod
+    def serial_number(self) -> int:
+        """
+        Returns certificate serial number
+        """
+
+    @property
+    @abc.abstractmethod
+    def version(self) -> Version:
+        """
+        Returns the certificate version
+        """
+
+    @abc.abstractmethod
+    def public_key(self) -> CertificatePublicKeyTypes:
+        """
+        Returns the public key
+        """
+
+    @property
+    @abc.abstractmethod
+    def public_key_algorithm_oid(self) -> ObjectIdentifier:
+        """
+        Returns the ObjectIdentifier of the public key.
+        """
+
+    @property
+    @abc.abstractmethod
+    def not_valid_before(self) -> datetime.datetime:
+        """
+        Not before time (represented as UTC datetime)
+        """
+
+    @property
+    @abc.abstractmethod
+    def not_valid_before_utc(self) -> datetime.datetime:
+        """
+        Not before time (represented as a non-naive UTC datetime)
+        """
+
+    @property
+    @abc.abstractmethod
+    def not_valid_after(self) -> datetime.datetime:
+        """
+        Not after time (represented as UTC datetime)
+        """
+
+    @property
+    @abc.abstractmethod
+    def not_valid_after_utc(self) -> datetime.datetime:
+        """
+        Not after time (represented as a non-naive UTC datetime)
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer(self) -> Name:
+        """
+        Returns the issuer name object.
+        """
+
+    @property
+    @abc.abstractmethod
+    def subject(self) -> Name:
+        """
+        Returns the subject name object.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_hash_algorithm(
+        self,
+    ) -> hashes.HashAlgorithm | None:
+        """
+        Returns a HashAlgorithm corresponding to the type of the digest signed
+        in the certificate.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_oid(self) -> ObjectIdentifier:
+        """
+        Returns the ObjectIdentifier of the signature algorithm.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_parameters(
+        self,
+    ) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA:
+        """
+        Returns the signature algorithm parameters.
+        """
+
+    @property
+    @abc.abstractmethod
+    def extensions(self) -> Extensions:
+        """
+        Returns an Extensions object.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature(self) -> bytes:
+        """
+        Returns the signature bytes.
+        """
+
+    @property
+    @abc.abstractmethod
+    def tbs_certificate_bytes(self) -> bytes:
+        """
+        Returns the tbsCertificate payload bytes as defined in RFC 5280.
+        """
+
+    @property
+    @abc.abstractmethod
+    def tbs_precertificate_bytes(self) -> bytes:
+        """
+        Returns the tbsCertificate payload bytes with the SCT list extension
+        stripped.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+    @abc.abstractmethod
+    def __hash__(self) -> int:
+        """
+        Computes a hash.
+        """
+
+    @abc.abstractmethod
+    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
+        """
+        Serializes the certificate to PEM or DER format.
+        """
+
+    @abc.abstractmethod
+    def verify_directly_issued_by(self, issuer: Certificate) -> None:
+        """
+        This method verifies that certificate issuer name matches the
+        issuer subject name and that the certificate is signed by the
+        issuer's private key. No other validation is performed.
+        """
+
+
+# Runtime isinstance checks need this since the rust class is not a subclass.
+Certificate.register(rust_x509.Certificate)
+
+
+class RevokedCertificate(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def serial_number(self) -> int:
+        """
+        Returns the serial number of the revoked certificate.
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_date(self) -> datetime.datetime:
+        """
+        Returns the date of when this certificate was revoked.
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_date_utc(self) -> datetime.datetime:
+        """
+        Returns the date of when this certificate was revoked as a non-naive
+        UTC datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def extensions(self) -> Extensions:
+        """
+        Returns an Extensions object containing a list of Revoked extensions.
+        """
+
+
+# Runtime isinstance checks need this since the rust class is not a subclass.
+RevokedCertificate.register(rust_x509.RevokedCertificate)
+
+
+class _RawRevokedCertificate(RevokedCertificate):
+    def __init__(
+        self,
+        serial_number: int,
+        revocation_date: datetime.datetime,
+        extensions: Extensions,
+    ):
+        self._serial_number = serial_number
+        self._revocation_date = revocation_date
+        self._extensions = extensions
+
+    @property
+    def serial_number(self) -> int:
+        return self._serial_number
+
+    @property
+    def revocation_date(self) -> datetime.datetime:
+        warnings.warn(
+            "Properties that return a naïve datetime object have been "
+            "deprecated. Please switch to revocation_date_utc.",
+            utils.DeprecatedIn42,
+            stacklevel=2,
+        )
+        return self._revocation_date
+
+    @property
+    def revocation_date_utc(self) -> datetime.datetime:
+        return self._revocation_date.replace(tzinfo=datetime.timezone.utc)
+
+    @property
+    def extensions(self) -> Extensions:
+        return self._extensions
+
+
+class CertificateRevocationList(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
+        """
+        Serializes the CRL to PEM or DER format.
+        """
+
+    @abc.abstractmethod
+    def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes:
+        """
+        Returns bytes using digest passed.
+        """
+
+    @abc.abstractmethod
+    def get_revoked_certificate_by_serial_number(
+        self, serial_number: int
+    ) -> RevokedCertificate | None:
+        """
+        Returns an instance of RevokedCertificate or None if the serial_number
+        is not in the CRL.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_hash_algorithm(
+        self,
+    ) -> hashes.HashAlgorithm | None:
+        """
+        Returns a HashAlgorithm corresponding to the type of the digest signed
+        in the certificate.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_oid(self) -> ObjectIdentifier:
+        """
+        Returns the ObjectIdentifier of the signature algorithm.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_parameters(
+        self,
+    ) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA:
+        """
+        Returns the signature algorithm parameters.
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer(self) -> Name:
+        """
+        Returns the X509Name with the issuer of this CRL.
+        """
+
+    @property
+    @abc.abstractmethod
+    def next_update(self) -> datetime.datetime | None:
+        """
+        Returns the date of next update for this CRL.
+        """
+
+    @property
+    @abc.abstractmethod
+    def next_update_utc(self) -> datetime.datetime | None:
+        """
+        Returns the date of next update for this CRL as a non-naive UTC
+        datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def last_update(self) -> datetime.datetime:
+        """
+        Returns the date of last update for this CRL.
+        """
+
+    @property
+    @abc.abstractmethod
+    def last_update_utc(self) -> datetime.datetime:
+        """
+        Returns the date of last update for this CRL as a non-naive UTC
+        datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def extensions(self) -> Extensions:
+        """
+        Returns an Extensions object containing a list of CRL extensions.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature(self) -> bytes:
+        """
+        Returns the signature bytes.
+        """
+
+    @property
+    @abc.abstractmethod
+    def tbs_certlist_bytes(self) -> bytes:
+        """
+        Returns the tbsCertList payload bytes as defined in RFC 5280.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+    @abc.abstractmethod
+    def __len__(self) -> int:
+        """
+        Number of revoked certificates in the CRL.
+        """
+
+    @typing.overload
+    def __getitem__(self, idx: int) -> RevokedCertificate: ...
+
+    @typing.overload
+    def __getitem__(self, idx: slice) -> list[RevokedCertificate]: ...
+
+    @abc.abstractmethod
+    def __getitem__(
+        self, idx: int | slice
+    ) -> RevokedCertificate | list[RevokedCertificate]:
+        """
+        Returns a revoked certificate (or slice of revoked certificates).
+        """
+
+    @abc.abstractmethod
+    def __iter__(self) -> typing.Iterator[RevokedCertificate]:
+        """
+        Iterator over the revoked certificates
+        """
+
+    @abc.abstractmethod
+    def is_signature_valid(
+        self, public_key: CertificateIssuerPublicKeyTypes
+    ) -> bool:
+        """
+        Verifies signature of revocation list against given public key.
+        """
+
+
+CertificateRevocationList.register(rust_x509.CertificateRevocationList)
+
+
+class CertificateSigningRequest(metaclass=abc.ABCMeta):
+    @abc.abstractmethod
+    def __eq__(self, other: object) -> bool:
+        """
+        Checks equality.
+        """
+
+    @abc.abstractmethod
+    def __hash__(self) -> int:
+        """
+        Computes a hash.
+        """
+
+    @abc.abstractmethod
+    def public_key(self) -> CertificatePublicKeyTypes:
+        """
+        Returns the public key
+        """
+
+    @property
+    @abc.abstractmethod
+    def subject(self) -> Name:
+        """
+        Returns the subject name object.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_hash_algorithm(
+        self,
+    ) -> hashes.HashAlgorithm | None:
+        """
+        Returns a HashAlgorithm corresponding to the type of the digest signed
+        in the certificate.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_oid(self) -> ObjectIdentifier:
+        """
+        Returns the ObjectIdentifier of the signature algorithm.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_parameters(
+        self,
+    ) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA:
+        """
+        Returns the signature algorithm parameters.
+        """
+
+    @property
+    @abc.abstractmethod
+    def extensions(self) -> Extensions:
+        """
+        Returns the extensions in the signing request.
+        """
+
+    @property
+    @abc.abstractmethod
+    def attributes(self) -> Attributes:
+        """
+        Returns an Attributes object.
+        """
+
+    @abc.abstractmethod
+    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
+        """
+        Encodes the request to PEM or DER format.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature(self) -> bytes:
+        """
+        Returns the signature bytes.
+        """
+
+    @property
+    @abc.abstractmethod
+    def tbs_certrequest_bytes(self) -> bytes:
+        """
+        Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC
+        2986.
+        """
+
+    @property
+    @abc.abstractmethod
+    def is_signature_valid(self) -> bool:
+        """
+        Verifies signature of signing request.
+        """
+
+    @abc.abstractmethod
+    def get_attribute_for_oid(self, oid: ObjectIdentifier) -> bytes:
+        """
+        Get the attribute value for a given OID.
+        """
+
+
+# Runtime isinstance checks need this since the rust class is not a subclass.
+CertificateSigningRequest.register(rust_x509.CertificateSigningRequest)
+
+
+load_pem_x509_certificate = rust_x509.load_pem_x509_certificate
+load_der_x509_certificate = rust_x509.load_der_x509_certificate
+
+load_pem_x509_certificates = rust_x509.load_pem_x509_certificates
+
+load_pem_x509_csr = rust_x509.load_pem_x509_csr
+load_der_x509_csr = rust_x509.load_der_x509_csr
+
+load_pem_x509_crl = rust_x509.load_pem_x509_crl
+load_der_x509_crl = rust_x509.load_der_x509_crl
+
+
+class CertificateSigningRequestBuilder:
+    def __init__(
+        self,
+        subject_name: Name | None = None,
+        extensions: list[Extension[ExtensionType]] = [],
+        attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [],
+    ):
+        """
+        Creates an empty X.509 certificate request (v1).
+        """
+        self._subject_name = subject_name
+        self._extensions = extensions
+        self._attributes = attributes
+
+    def subject_name(self, name: Name) -> CertificateSigningRequestBuilder:
+        """
+        Sets the certificate requestor's distinguished name.
+        """
+        if not isinstance(name, Name):
+            raise TypeError("Expecting x509.Name object.")
+        if self._subject_name is not None:
+            raise ValueError("The subject name may only be set once.")
+        return CertificateSigningRequestBuilder(
+            name, self._extensions, self._attributes
+        )
+
+    def add_extension(
+        self, extval: ExtensionType, critical: bool
+    ) -> CertificateSigningRequestBuilder:
+        """
+        Adds an X.509 extension to the certificate request.
+        """
+        if not isinstance(extval, ExtensionType):
+            raise TypeError("extension must be an ExtensionType")
+
+        extension = Extension(extval.oid, critical, extval)
+        _reject_duplicate_extension(extension, self._extensions)
+
+        return CertificateSigningRequestBuilder(
+            self._subject_name,
+            [*self._extensions, extension],
+            self._attributes,
+        )
+
+    def add_attribute(
+        self,
+        oid: ObjectIdentifier,
+        value: bytes,
+        *,
+        _tag: _ASN1Type | None = None,
+    ) -> CertificateSigningRequestBuilder:
+        """
+        Adds an X.509 attribute with an OID and associated value.
+        """
+        if not isinstance(oid, ObjectIdentifier):
+            raise TypeError("oid must be an ObjectIdentifier")
+
+        if not isinstance(value, bytes):
+            raise TypeError("value must be bytes")
+
+        if _tag is not None and not isinstance(_tag, _ASN1Type):
+            raise TypeError("tag must be _ASN1Type")
+
+        _reject_duplicate_attribute(oid, self._attributes)
+
+        if _tag is not None:
+            tag = _tag.value
+        else:
+            tag = None
+
+        return CertificateSigningRequestBuilder(
+            self._subject_name,
+            self._extensions,
+            [*self._attributes, (oid, value, tag)],
+        )
+
+    def sign(
+        self,
+        private_key: CertificateIssuerPrivateKeyTypes,
+        algorithm: _AllowedHashTypes | None,
+        backend: typing.Any = None,
+        *,
+        rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+    ) -> CertificateSigningRequest:
+        """
+        Signs the request using the requestor's private key.
+        """
+        if self._subject_name is None:
+            raise ValueError("A CertificateSigningRequest must have a subject")
+
+        if rsa_padding is not None:
+            if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+                raise TypeError("Padding must be PSS or PKCS1v15")
+            if not isinstance(private_key, rsa.RSAPrivateKey):
+                raise TypeError("Padding is only supported for RSA keys")
+
+        return rust_x509.create_x509_csr(
+            self, private_key, algorithm, rsa_padding
+        )
+
+
+class CertificateBuilder:
+    _extensions: list[Extension[ExtensionType]]
+
+    def __init__(
+        self,
+        issuer_name: Name | None = None,
+        subject_name: Name | None = None,
+        public_key: CertificatePublicKeyTypes | None = None,
+        serial_number: int | None = None,
+        not_valid_before: datetime.datetime | None = None,
+        not_valid_after: datetime.datetime | None = None,
+        extensions: list[Extension[ExtensionType]] = [],
+    ) -> None:
+        self._version = Version.v3
+        self._issuer_name = issuer_name
+        self._subject_name = subject_name
+        self._public_key = public_key
+        self._serial_number = serial_number
+        self._not_valid_before = not_valid_before
+        self._not_valid_after = not_valid_after
+        self._extensions = extensions
+
+    def issuer_name(self, name: Name) -> CertificateBuilder:
+        """
+        Sets the CA's distinguished name.
+        """
+        if not isinstance(name, Name):
+            raise TypeError("Expecting x509.Name object.")
+        if self._issuer_name is not None:
+            raise ValueError("The issuer name may only be set once.")
+        return CertificateBuilder(
+            name,
+            self._subject_name,
+            self._public_key,
+            self._serial_number,
+            self._not_valid_before,
+            self._not_valid_after,
+            self._extensions,
+        )
+
+    def subject_name(self, name: Name) -> CertificateBuilder:
+        """
+        Sets the requestor's distinguished name.
+        """
+        if not isinstance(name, Name):
+            raise TypeError("Expecting x509.Name object.")
+        if self._subject_name is not None:
+            raise ValueError("The subject name may only be set once.")
+        return CertificateBuilder(
+            self._issuer_name,
+            name,
+            self._public_key,
+            self._serial_number,
+            self._not_valid_before,
+            self._not_valid_after,
+            self._extensions,
+        )
+
+    def public_key(
+        self,
+        key: CertificatePublicKeyTypes,
+    ) -> CertificateBuilder:
+        """
+        Sets the requestor's public key (as found in the signing request).
+        """
+        if not isinstance(
+            key,
+            (
+                dsa.DSAPublicKey,
+                rsa.RSAPublicKey,
+                ec.EllipticCurvePublicKey,
+                ed25519.Ed25519PublicKey,
+                ed448.Ed448PublicKey,
+                x25519.X25519PublicKey,
+                x448.X448PublicKey,
+            ),
+        ):
+            raise TypeError(
+                "Expecting one of DSAPublicKey, RSAPublicKey,"
+                " EllipticCurvePublicKey, Ed25519PublicKey,"
+                " Ed448PublicKey, X25519PublicKey, or "
+                "X448PublicKey."
+            )
+        if self._public_key is not None:
+            raise ValueError("The public key may only be set once.")
+        return CertificateBuilder(
+            self._issuer_name,
+            self._subject_name,
+            key,
+            self._serial_number,
+            self._not_valid_before,
+            self._not_valid_after,
+            self._extensions,
+        )
+
+    def serial_number(self, number: int) -> CertificateBuilder:
+        """
+        Sets the certificate serial number.
+        """
+        if not isinstance(number, int):
+            raise TypeError("Serial number must be of integral type.")
+        if self._serial_number is not None:
+            raise ValueError("The serial number may only be set once.")
+        if number <= 0:
+            raise ValueError("The serial number should be positive.")
+
+        # ASN.1 integers are always signed, so most significant bit must be
+        # zero.
+        if number.bit_length() >= 160:  # As defined in RFC 5280
+            raise ValueError(
+                "The serial number should not be more than 159 bits."
+            )
+        return CertificateBuilder(
+            self._issuer_name,
+            self._subject_name,
+            self._public_key,
+            number,
+            self._not_valid_before,
+            self._not_valid_after,
+            self._extensions,
+        )
+
+    def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder:
+        """
+        Sets the certificate activation time.
+        """
+        if not isinstance(time, datetime.datetime):
+            raise TypeError("Expecting datetime object.")
+        if self._not_valid_before is not None:
+            raise ValueError("The not valid before may only be set once.")
+        time = _convert_to_naive_utc_time(time)
+        if time < _EARLIEST_UTC_TIME:
+            raise ValueError(
+                "The not valid before date must be on or after"
+                " 1950 January 1)."
+            )
+        if self._not_valid_after is not None and time > self._not_valid_after:
+            raise ValueError(
+                "The not valid before date must be before the not valid after "
+                "date."
+            )
+        return CertificateBuilder(
+            self._issuer_name,
+            self._subject_name,
+            self._public_key,
+            self._serial_number,
+            time,
+            self._not_valid_after,
+            self._extensions,
+        )
+
+    def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder:
+        """
+        Sets the certificate expiration time.
+        """
+        if not isinstance(time, datetime.datetime):
+            raise TypeError("Expecting datetime object.")
+        if self._not_valid_after is not None:
+            raise ValueError("The not valid after may only be set once.")
+        time = _convert_to_naive_utc_time(time)
+        if time < _EARLIEST_UTC_TIME:
+            raise ValueError(
+                "The not valid after date must be on or after"
+                " 1950 January 1."
+            )
+        if (
+            self._not_valid_before is not None
+            and time < self._not_valid_before
+        ):
+            raise ValueError(
+                "The not valid after date must be after the not valid before "
+                "date."
+            )
+        return CertificateBuilder(
+            self._issuer_name,
+            self._subject_name,
+            self._public_key,
+            self._serial_number,
+            self._not_valid_before,
+            time,
+            self._extensions,
+        )
+
+    def add_extension(
+        self, extval: ExtensionType, critical: bool
+    ) -> CertificateBuilder:
+        """
+        Adds an X.509 extension to the certificate.
+        """
+        if not isinstance(extval, ExtensionType):
+            raise TypeError("extension must be an ExtensionType")
+
+        extension = Extension(extval.oid, critical, extval)
+        _reject_duplicate_extension(extension, self._extensions)
+
+        return CertificateBuilder(
+            self._issuer_name,
+            self._subject_name,
+            self._public_key,
+            self._serial_number,
+            self._not_valid_before,
+            self._not_valid_after,
+            [*self._extensions, extension],
+        )
+
+    def sign(
+        self,
+        private_key: CertificateIssuerPrivateKeyTypes,
+        algorithm: _AllowedHashTypes | None,
+        backend: typing.Any = None,
+        *,
+        rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+    ) -> Certificate:
+        """
+        Signs the certificate using the CA's private key.
+        """
+        if self._subject_name is None:
+            raise ValueError("A certificate must have a subject name")
+
+        if self._issuer_name is None:
+            raise ValueError("A certificate must have an issuer name")
+
+        if self._serial_number is None:
+            raise ValueError("A certificate must have a serial number")
+
+        if self._not_valid_before is None:
+            raise ValueError("A certificate must have a not valid before time")
+
+        if self._not_valid_after is None:
+            raise ValueError("A certificate must have a not valid after time")
+
+        if self._public_key is None:
+            raise ValueError("A certificate must have a public key")
+
+        if rsa_padding is not None:
+            if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+                raise TypeError("Padding must be PSS or PKCS1v15")
+            if not isinstance(private_key, rsa.RSAPrivateKey):
+                raise TypeError("Padding is only supported for RSA keys")
+
+        return rust_x509.create_x509_certificate(
+            self, private_key, algorithm, rsa_padding
+        )
+
+
+class CertificateRevocationListBuilder:
+    _extensions: list[Extension[ExtensionType]]
+    _revoked_certificates: list[RevokedCertificate]
+
+    def __init__(
+        self,
+        issuer_name: Name | None = None,
+        last_update: datetime.datetime | None = None,
+        next_update: datetime.datetime | None = None,
+        extensions: list[Extension[ExtensionType]] = [],
+        revoked_certificates: list[RevokedCertificate] = [],
+    ):
+        self._issuer_name = issuer_name
+        self._last_update = last_update
+        self._next_update = next_update
+        self._extensions = extensions
+        self._revoked_certificates = revoked_certificates
+
+    def issuer_name(
+        self, issuer_name: Name
+    ) -> CertificateRevocationListBuilder:
+        if not isinstance(issuer_name, Name):
+            raise TypeError("Expecting x509.Name object.")
+        if self._issuer_name is not None:
+            raise ValueError("The issuer name may only be set once.")
+        return CertificateRevocationListBuilder(
+            issuer_name,
+            self._last_update,
+            self._next_update,
+            self._extensions,
+            self._revoked_certificates,
+        )
+
+    def last_update(
+        self, last_update: datetime.datetime
+    ) -> CertificateRevocationListBuilder:
+        if not isinstance(last_update, datetime.datetime):
+            raise TypeError("Expecting datetime object.")
+        if self._last_update is not None:
+            raise ValueError("Last update may only be set once.")
+        last_update = _convert_to_naive_utc_time(last_update)
+        if last_update < _EARLIEST_UTC_TIME:
+            raise ValueError(
+                "The last update date must be on or after 1950 January 1."
+            )
+        if self._next_update is not None and last_update > self._next_update:
+            raise ValueError(
+                "The last update date must be before the next update date."
+            )
+        return CertificateRevocationListBuilder(
+            self._issuer_name,
+            last_update,
+            self._next_update,
+            self._extensions,
+            self._revoked_certificates,
+        )
+
+    def next_update(
+        self, next_update: datetime.datetime
+    ) -> CertificateRevocationListBuilder:
+        if not isinstance(next_update, datetime.datetime):
+            raise TypeError("Expecting datetime object.")
+        if self._next_update is not None:
+            raise ValueError("Last update may only be set once.")
+        next_update = _convert_to_naive_utc_time(next_update)
+        if next_update < _EARLIEST_UTC_TIME:
+            raise ValueError(
+                "The last update date must be on or after 1950 January 1."
+            )
+        if self._last_update is not None and next_update < self._last_update:
+            raise ValueError(
+                "The next update date must be after the last update date."
+            )
+        return CertificateRevocationListBuilder(
+            self._issuer_name,
+            self._last_update,
+            next_update,
+            self._extensions,
+            self._revoked_certificates,
+        )
+
+    def add_extension(
+        self, extval: ExtensionType, critical: bool
+    ) -> CertificateRevocationListBuilder:
+        """
+        Adds an X.509 extension to the certificate revocation list.
+        """
+        if not isinstance(extval, ExtensionType):
+            raise TypeError("extension must be an ExtensionType")
+
+        extension = Extension(extval.oid, critical, extval)
+        _reject_duplicate_extension(extension, self._extensions)
+        return CertificateRevocationListBuilder(
+            self._issuer_name,
+            self._last_update,
+            self._next_update,
+            [*self._extensions, extension],
+            self._revoked_certificates,
+        )
+
+    def add_revoked_certificate(
+        self, revoked_certificate: RevokedCertificate
+    ) -> CertificateRevocationListBuilder:
+        """
+        Adds a revoked certificate to the CRL.
+        """
+        if not isinstance(revoked_certificate, RevokedCertificate):
+            raise TypeError("Must be an instance of RevokedCertificate")
+
+        return CertificateRevocationListBuilder(
+            self._issuer_name,
+            self._last_update,
+            self._next_update,
+            self._extensions,
+            [*self._revoked_certificates, revoked_certificate],
+        )
+
+    def sign(
+        self,
+        private_key: CertificateIssuerPrivateKeyTypes,
+        algorithm: _AllowedHashTypes | None,
+        backend: typing.Any = None,
+        *,
+        rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+    ) -> CertificateRevocationList:
+        if self._issuer_name is None:
+            raise ValueError("A CRL must have an issuer name")
+
+        if self._last_update is None:
+            raise ValueError("A CRL must have a last update time")
+
+        if self._next_update is None:
+            raise ValueError("A CRL must have a next update time")
+
+        if rsa_padding is not None:
+            if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+                raise TypeError("Padding must be PSS or PKCS1v15")
+            if not isinstance(private_key, rsa.RSAPrivateKey):
+                raise TypeError("Padding is only supported for RSA keys")
+
+        return rust_x509.create_x509_crl(
+            self, private_key, algorithm, rsa_padding
+        )
+
+
+class RevokedCertificateBuilder:
+    def __init__(
+        self,
+        serial_number: int | None = None,
+        revocation_date: datetime.datetime | None = None,
+        extensions: list[Extension[ExtensionType]] = [],
+    ):
+        self._serial_number = serial_number
+        self._revocation_date = revocation_date
+        self._extensions = extensions
+
+    def serial_number(self, number: int) -> RevokedCertificateBuilder:
+        if not isinstance(number, int):
+            raise TypeError("Serial number must be of integral type.")
+        if self._serial_number is not None:
+            raise ValueError("The serial number may only be set once.")
+        if number <= 0:
+            raise ValueError("The serial number should be positive")
+
+        # ASN.1 integers are always signed, so most significant bit must be
+        # zero.
+        if number.bit_length() >= 160:  # As defined in RFC 5280
+            raise ValueError(
+                "The serial number should not be more than 159 bits."
+            )
+        return RevokedCertificateBuilder(
+            number, self._revocation_date, self._extensions
+        )
+
+    def revocation_date(
+        self, time: datetime.datetime
+    ) -> RevokedCertificateBuilder:
+        if not isinstance(time, datetime.datetime):
+            raise TypeError("Expecting datetime object.")
+        if self._revocation_date is not None:
+            raise ValueError("The revocation date may only be set once.")
+        time = _convert_to_naive_utc_time(time)
+        if time < _EARLIEST_UTC_TIME:
+            raise ValueError(
+                "The revocation date must be on or after 1950 January 1."
+            )
+        return RevokedCertificateBuilder(
+            self._serial_number, time, self._extensions
+        )
+
+    def add_extension(
+        self, extval: ExtensionType, critical: bool
+    ) -> RevokedCertificateBuilder:
+        if not isinstance(extval, ExtensionType):
+            raise TypeError("extension must be an ExtensionType")
+
+        extension = Extension(extval.oid, critical, extval)
+        _reject_duplicate_extension(extension, self._extensions)
+        return RevokedCertificateBuilder(
+            self._serial_number,
+            self._revocation_date,
+            [*self._extensions, extension],
+        )
+
+    def build(self, backend: typing.Any = None) -> RevokedCertificate:
+        if self._serial_number is None:
+            raise ValueError("A revoked certificate must have a serial number")
+        if self._revocation_date is None:
+            raise ValueError(
+                "A revoked certificate must have a revocation date"
+            )
+        return _RawRevokedCertificate(
+            self._serial_number,
+            self._revocation_date,
+            Extensions(self._extensions),
+        )
+
+
+def random_serial_number() -> int:
+    return int.from_bytes(os.urandom(20), "big") >> 1
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/certificate_transparency.py b/.venv/lib/python3.8/site-packages/cryptography/x509/certificate_transparency.py
new file mode 100644
index 0000000000000000000000000000000000000000..73647ee716fc1c36bf255347617308522b724e5d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/certificate_transparency.py
@@ -0,0 +1,97 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import datetime
+
+from cryptography import utils
+from cryptography.hazmat.bindings._rust import x509 as rust_x509
+from cryptography.hazmat.primitives.hashes import HashAlgorithm
+
+
+class LogEntryType(utils.Enum):
+    X509_CERTIFICATE = 0
+    PRE_CERTIFICATE = 1
+
+
+class Version(utils.Enum):
+    v1 = 0
+
+
+class SignatureAlgorithm(utils.Enum):
+    """
+    Signature algorithms that are valid for SCTs.
+
+    These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2).
+
+    See: <https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.4.1>
+    """
+
+    ANONYMOUS = 0
+    RSA = 1
+    DSA = 2
+    ECDSA = 3
+
+
+class SignedCertificateTimestamp(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def version(self) -> Version:
+        """
+        Returns the SCT version.
+        """
+
+    @property
+    @abc.abstractmethod
+    def log_id(self) -> bytes:
+        """
+        Returns an identifier indicating which log this SCT is for.
+        """
+
+    @property
+    @abc.abstractmethod
+    def timestamp(self) -> datetime.datetime:
+        """
+        Returns the timestamp for this SCT.
+        """
+
+    @property
+    @abc.abstractmethod
+    def entry_type(self) -> LogEntryType:
+        """
+        Returns whether this is an SCT for a certificate or pre-certificate.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_hash_algorithm(self) -> HashAlgorithm:
+        """
+        Returns the hash algorithm used for the SCT's signature.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm(self) -> SignatureAlgorithm:
+        """
+        Returns the signing algorithm used for the SCT's signature.
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature(self) -> bytes:
+        """
+        Returns the signature for this SCT.
+        """
+
+    @property
+    @abc.abstractmethod
+    def extension_bytes(self) -> bytes:
+        """
+        Returns the raw bytes of any extensions for this SCT.
+        """
+
+
+SignedCertificateTimestamp.register(rust_x509.Sct)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/extensions.py b/.venv/lib/python3.8/site-packages/cryptography/x509/extensions.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e7486a594edfd93aa4217822f8cb3ac7b25392c
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/extensions.py
@@ -0,0 +1,2196 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import datetime
+import hashlib
+import ipaddress
+import typing
+
+from cryptography import utils
+from cryptography.hazmat.bindings._rust import asn1
+from cryptography.hazmat.bindings._rust import x509 as rust_x509
+from cryptography.hazmat.primitives import constant_time, serialization
+from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
+from cryptography.hazmat.primitives.asymmetric.types import (
+    CertificateIssuerPublicKeyTypes,
+    CertificatePublicKeyTypes,
+)
+from cryptography.x509.certificate_transparency import (
+    SignedCertificateTimestamp,
+)
+from cryptography.x509.general_name import (
+    DirectoryName,
+    DNSName,
+    GeneralName,
+    IPAddress,
+    OtherName,
+    RegisteredID,
+    RFC822Name,
+    UniformResourceIdentifier,
+    _IPAddressTypes,
+)
+from cryptography.x509.name import Name, RelativeDistinguishedName
+from cryptography.x509.oid import (
+    CRLEntryExtensionOID,
+    ExtensionOID,
+    ObjectIdentifier,
+    OCSPExtensionOID,
+)
+
+ExtensionTypeVar = typing.TypeVar(
+    "ExtensionTypeVar", bound="ExtensionType", covariant=True
+)
+
+
+def _key_identifier_from_public_key(
+    public_key: CertificatePublicKeyTypes,
+) -> bytes:
+    if isinstance(public_key, RSAPublicKey):
+        data = public_key.public_bytes(
+            serialization.Encoding.DER,
+            serialization.PublicFormat.PKCS1,
+        )
+    elif isinstance(public_key, EllipticCurvePublicKey):
+        data = public_key.public_bytes(
+            serialization.Encoding.X962,
+            serialization.PublicFormat.UncompressedPoint,
+        )
+    else:
+        # This is a very slow way to do this.
+        serialized = public_key.public_bytes(
+            serialization.Encoding.DER,
+            serialization.PublicFormat.SubjectPublicKeyInfo,
+        )
+        data = asn1.parse_spki_for_data(serialized)
+
+    return hashlib.sha1(data).digest()
+
+
+def _make_sequence_methods(field_name: str):
+    def len_method(self) -> int:
+        return len(getattr(self, field_name))
+
+    def iter_method(self):
+        return iter(getattr(self, field_name))
+
+    def getitem_method(self, idx):
+        return getattr(self, field_name)[idx]
+
+    return len_method, iter_method, getitem_method
+
+
+class DuplicateExtension(Exception):
+    def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
+        super().__init__(msg)
+        self.oid = oid
+
+
+class ExtensionNotFound(Exception):
+    def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
+        super().__init__(msg)
+        self.oid = oid
+
+
+class ExtensionType(metaclass=abc.ABCMeta):
+    oid: typing.ClassVar[ObjectIdentifier]
+
+    def public_bytes(self) -> bytes:
+        """
+        Serializes the extension type to DER.
+        """
+        raise NotImplementedError(
+            f"public_bytes is not implemented for extension type {self!r}"
+        )
+
+
+class Extensions:
+    def __init__(
+        self, extensions: typing.Iterable[Extension[ExtensionType]]
+    ) -> None:
+        self._extensions = list(extensions)
+
+    def get_extension_for_oid(
+        self, oid: ObjectIdentifier
+    ) -> Extension[ExtensionType]:
+        for ext in self:
+            if ext.oid == oid:
+                return ext
+
+        raise ExtensionNotFound(f"No {oid} extension was found", oid)
+
+    def get_extension_for_class(
+        self, extclass: type[ExtensionTypeVar]
+    ) -> Extension[ExtensionTypeVar]:
+        if extclass is UnrecognizedExtension:
+            raise TypeError(
+                "UnrecognizedExtension can't be used with "
+                "get_extension_for_class because more than one instance of the"
+                " class may be present."
+            )
+
+        for ext in self:
+            if isinstance(ext.value, extclass):
+                return ext
+
+        raise ExtensionNotFound(
+            f"No {extclass} extension was found", extclass.oid
+        )
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions")
+
+    def __repr__(self) -> str:
+        return f"<Extensions({self._extensions})>"
+
+
+class CRLNumber(ExtensionType):
+    oid = ExtensionOID.CRL_NUMBER
+
+    def __init__(self, crl_number: int) -> None:
+        if not isinstance(crl_number, int):
+            raise TypeError("crl_number must be an integer")
+
+        self._crl_number = crl_number
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, CRLNumber):
+            return NotImplemented
+
+        return self.crl_number == other.crl_number
+
+    def __hash__(self) -> int:
+        return hash(self.crl_number)
+
+    def __repr__(self) -> str:
+        return f"<CRLNumber({self.crl_number})>"
+
+    @property
+    def crl_number(self) -> int:
+        return self._crl_number
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class AuthorityKeyIdentifier(ExtensionType):
+    oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER
+
+    def __init__(
+        self,
+        key_identifier: bytes | None,
+        authority_cert_issuer: typing.Iterable[GeneralName] | None,
+        authority_cert_serial_number: int | None,
+    ) -> None:
+        if (authority_cert_issuer is None) != (
+            authority_cert_serial_number is None
+        ):
+            raise ValueError(
+                "authority_cert_issuer and authority_cert_serial_number "
+                "must both be present or both None"
+            )
+
+        if authority_cert_issuer is not None:
+            authority_cert_issuer = list(authority_cert_issuer)
+            if not all(
+                isinstance(x, GeneralName) for x in authority_cert_issuer
+            ):
+                raise TypeError(
+                    "authority_cert_issuer must be a list of GeneralName "
+                    "objects"
+                )
+
+        if authority_cert_serial_number is not None and not isinstance(
+            authority_cert_serial_number, int
+        ):
+            raise TypeError("authority_cert_serial_number must be an integer")
+
+        self._key_identifier = key_identifier
+        self._authority_cert_issuer = authority_cert_issuer
+        self._authority_cert_serial_number = authority_cert_serial_number
+
+    # This takes a subset of CertificatePublicKeyTypes because an issuer
+    # cannot have an X25519/X448 key. This introduces some unfortunate
+    # asymmetry that requires typing users to explicitly
+    # narrow their type, but we should make this accurate and not just
+    # convenient.
+    @classmethod
+    def from_issuer_public_key(
+        cls, public_key: CertificateIssuerPublicKeyTypes
+    ) -> AuthorityKeyIdentifier:
+        digest = _key_identifier_from_public_key(public_key)
+        return cls(
+            key_identifier=digest,
+            authority_cert_issuer=None,
+            authority_cert_serial_number=None,
+        )
+
+    @classmethod
+    def from_issuer_subject_key_identifier(
+        cls, ski: SubjectKeyIdentifier
+    ) -> AuthorityKeyIdentifier:
+        return cls(
+            key_identifier=ski.digest,
+            authority_cert_issuer=None,
+            authority_cert_serial_number=None,
+        )
+
+    def __repr__(self) -> str:
+        return (
+            f"<AuthorityKeyIdentifier(key_identifier={self.key_identifier!r}, "
+            f"authority_cert_issuer={self.authority_cert_issuer}, "
+            f"authority_cert_serial_number={self.authority_cert_serial_number}"
+            ")>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, AuthorityKeyIdentifier):
+            return NotImplemented
+
+        return (
+            self.key_identifier == other.key_identifier
+            and self.authority_cert_issuer == other.authority_cert_issuer
+            and self.authority_cert_serial_number
+            == other.authority_cert_serial_number
+        )
+
+    def __hash__(self) -> int:
+        if self.authority_cert_issuer is None:
+            aci = None
+        else:
+            aci = tuple(self.authority_cert_issuer)
+        return hash(
+            (self.key_identifier, aci, self.authority_cert_serial_number)
+        )
+
+    @property
+    def key_identifier(self) -> bytes | None:
+        return self._key_identifier
+
+    @property
+    def authority_cert_issuer(
+        self,
+    ) -> list[GeneralName] | None:
+        return self._authority_cert_issuer
+
+    @property
+    def authority_cert_serial_number(self) -> int | None:
+        return self._authority_cert_serial_number
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class SubjectKeyIdentifier(ExtensionType):
+    oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER
+
+    def __init__(self, digest: bytes) -> None:
+        self._digest = digest
+
+    @classmethod
+    def from_public_key(
+        cls, public_key: CertificatePublicKeyTypes
+    ) -> SubjectKeyIdentifier:
+        return cls(_key_identifier_from_public_key(public_key))
+
+    @property
+    def digest(self) -> bytes:
+        return self._digest
+
+    @property
+    def key_identifier(self) -> bytes:
+        return self._digest
+
+    def __repr__(self) -> str:
+        return f"<SubjectKeyIdentifier(digest={self.digest!r})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SubjectKeyIdentifier):
+            return NotImplemented
+
+        return constant_time.bytes_eq(self.digest, other.digest)
+
+    def __hash__(self) -> int:
+        return hash(self.digest)
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class AuthorityInformationAccess(ExtensionType):
+    oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS
+
+    def __init__(
+        self, descriptions: typing.Iterable[AccessDescription]
+    ) -> None:
+        descriptions = list(descriptions)
+        if not all(isinstance(x, AccessDescription) for x in descriptions):
+            raise TypeError(
+                "Every item in the descriptions list must be an "
+                "AccessDescription"
+            )
+
+        self._descriptions = descriptions
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
+
+    def __repr__(self) -> str:
+        return f"<AuthorityInformationAccess({self._descriptions})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, AuthorityInformationAccess):
+            return NotImplemented
+
+        return self._descriptions == other._descriptions
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._descriptions))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class SubjectInformationAccess(ExtensionType):
+    oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS
+
+    def __init__(
+        self, descriptions: typing.Iterable[AccessDescription]
+    ) -> None:
+        descriptions = list(descriptions)
+        if not all(isinstance(x, AccessDescription) for x in descriptions):
+            raise TypeError(
+                "Every item in the descriptions list must be an "
+                "AccessDescription"
+            )
+
+        self._descriptions = descriptions
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
+
+    def __repr__(self) -> str:
+        return f"<SubjectInformationAccess({self._descriptions})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SubjectInformationAccess):
+            return NotImplemented
+
+        return self._descriptions == other._descriptions
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._descriptions))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class AccessDescription:
+    def __init__(
+        self, access_method: ObjectIdentifier, access_location: GeneralName
+    ) -> None:
+        if not isinstance(access_method, ObjectIdentifier):
+            raise TypeError("access_method must be an ObjectIdentifier")
+
+        if not isinstance(access_location, GeneralName):
+            raise TypeError("access_location must be a GeneralName")
+
+        self._access_method = access_method
+        self._access_location = access_location
+
+    def __repr__(self) -> str:
+        return (
+            f"<AccessDescription(access_method={self.access_method}, "
+            f"access_location={self.access_location})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, AccessDescription):
+            return NotImplemented
+
+        return (
+            self.access_method == other.access_method
+            and self.access_location == other.access_location
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.access_method, self.access_location))
+
+    @property
+    def access_method(self) -> ObjectIdentifier:
+        return self._access_method
+
+    @property
+    def access_location(self) -> GeneralName:
+        return self._access_location
+
+
+class BasicConstraints(ExtensionType):
+    oid = ExtensionOID.BASIC_CONSTRAINTS
+
+    def __init__(self, ca: bool, path_length: int | None) -> None:
+        if not isinstance(ca, bool):
+            raise TypeError("ca must be a boolean value")
+
+        if path_length is not None and not ca:
+            raise ValueError("path_length must be None when ca is False")
+
+        if path_length is not None and (
+            not isinstance(path_length, int) or path_length < 0
+        ):
+            raise TypeError(
+                "path_length must be a non-negative integer or None"
+            )
+
+        self._ca = ca
+        self._path_length = path_length
+
+    @property
+    def ca(self) -> bool:
+        return self._ca
+
+    @property
+    def path_length(self) -> int | None:
+        return self._path_length
+
+    def __repr__(self) -> str:
+        return (
+            f"<BasicConstraints(ca={self.ca}, "
+            f"path_length={self.path_length})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, BasicConstraints):
+            return NotImplemented
+
+        return self.ca == other.ca and self.path_length == other.path_length
+
+    def __hash__(self) -> int:
+        return hash((self.ca, self.path_length))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class DeltaCRLIndicator(ExtensionType):
+    oid = ExtensionOID.DELTA_CRL_INDICATOR
+
+    def __init__(self, crl_number: int) -> None:
+        if not isinstance(crl_number, int):
+            raise TypeError("crl_number must be an integer")
+
+        self._crl_number = crl_number
+
+    @property
+    def crl_number(self) -> int:
+        return self._crl_number
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, DeltaCRLIndicator):
+            return NotImplemented
+
+        return self.crl_number == other.crl_number
+
+    def __hash__(self) -> int:
+        return hash(self.crl_number)
+
+    def __repr__(self) -> str:
+        return f"<DeltaCRLIndicator(crl_number={self.crl_number})>"
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class CRLDistributionPoints(ExtensionType):
+    oid = ExtensionOID.CRL_DISTRIBUTION_POINTS
+
+    def __init__(
+        self, distribution_points: typing.Iterable[DistributionPoint]
+    ) -> None:
+        distribution_points = list(distribution_points)
+        if not all(
+            isinstance(x, DistributionPoint) for x in distribution_points
+        ):
+            raise TypeError(
+                "distribution_points must be a list of DistributionPoint "
+                "objects"
+            )
+
+        self._distribution_points = distribution_points
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods(
+        "_distribution_points"
+    )
+
+    def __repr__(self) -> str:
+        return f"<CRLDistributionPoints({self._distribution_points})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, CRLDistributionPoints):
+            return NotImplemented
+
+        return self._distribution_points == other._distribution_points
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._distribution_points))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class FreshestCRL(ExtensionType):
+    oid = ExtensionOID.FRESHEST_CRL
+
+    def __init__(
+        self, distribution_points: typing.Iterable[DistributionPoint]
+    ) -> None:
+        distribution_points = list(distribution_points)
+        if not all(
+            isinstance(x, DistributionPoint) for x in distribution_points
+        ):
+            raise TypeError(
+                "distribution_points must be a list of DistributionPoint "
+                "objects"
+            )
+
+        self._distribution_points = distribution_points
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods(
+        "_distribution_points"
+    )
+
+    def __repr__(self) -> str:
+        return f"<FreshestCRL({self._distribution_points})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, FreshestCRL):
+            return NotImplemented
+
+        return self._distribution_points == other._distribution_points
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._distribution_points))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class DistributionPoint:
+    def __init__(
+        self,
+        full_name: typing.Iterable[GeneralName] | None,
+        relative_name: RelativeDistinguishedName | None,
+        reasons: frozenset[ReasonFlags] | None,
+        crl_issuer: typing.Iterable[GeneralName] | None,
+    ) -> None:
+        if full_name and relative_name:
+            raise ValueError(
+                "You cannot provide both full_name and relative_name, at "
+                "least one must be None."
+            )
+        if not full_name and not relative_name and not crl_issuer:
+            raise ValueError(
+                "Either full_name, relative_name or crl_issuer must be "
+                "provided."
+            )
+
+        if full_name is not None:
+            full_name = list(full_name)
+            if not all(isinstance(x, GeneralName) for x in full_name):
+                raise TypeError(
+                    "full_name must be a list of GeneralName objects"
+                )
+
+        if relative_name:
+            if not isinstance(relative_name, RelativeDistinguishedName):
+                raise TypeError(
+                    "relative_name must be a RelativeDistinguishedName"
+                )
+
+        if crl_issuer is not None:
+            crl_issuer = list(crl_issuer)
+            if not all(isinstance(x, GeneralName) for x in crl_issuer):
+                raise TypeError(
+                    "crl_issuer must be None or a list of general names"
+                )
+
+        if reasons and (
+            not isinstance(reasons, frozenset)
+            or not all(isinstance(x, ReasonFlags) for x in reasons)
+        ):
+            raise TypeError("reasons must be None or frozenset of ReasonFlags")
+
+        if reasons and (
+            ReasonFlags.unspecified in reasons
+            or ReasonFlags.remove_from_crl in reasons
+        ):
+            raise ValueError(
+                "unspecified and remove_from_crl are not valid reasons in a "
+                "DistributionPoint"
+            )
+
+        self._full_name = full_name
+        self._relative_name = relative_name
+        self._reasons = reasons
+        self._crl_issuer = crl_issuer
+
+    def __repr__(self) -> str:
+        return (
+            "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela"
+            "tive_name}, reasons={0.reasons}, "
+            "crl_issuer={0.crl_issuer})>".format(self)
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, DistributionPoint):
+            return NotImplemented
+
+        return (
+            self.full_name == other.full_name
+            and self.relative_name == other.relative_name
+            and self.reasons == other.reasons
+            and self.crl_issuer == other.crl_issuer
+        )
+
+    def __hash__(self) -> int:
+        if self.full_name is not None:
+            fn: tuple[GeneralName, ...] | None = tuple(self.full_name)
+        else:
+            fn = None
+
+        if self.crl_issuer is not None:
+            crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer)
+        else:
+            crl_issuer = None
+
+        return hash((fn, self.relative_name, self.reasons, crl_issuer))
+
+    @property
+    def full_name(self) -> list[GeneralName] | None:
+        return self._full_name
+
+    @property
+    def relative_name(self) -> RelativeDistinguishedName | None:
+        return self._relative_name
+
+    @property
+    def reasons(self) -> frozenset[ReasonFlags] | None:
+        return self._reasons
+
+    @property
+    def crl_issuer(self) -> list[GeneralName] | None:
+        return self._crl_issuer
+
+
+class ReasonFlags(utils.Enum):
+    unspecified = "unspecified"
+    key_compromise = "keyCompromise"
+    ca_compromise = "cACompromise"
+    affiliation_changed = "affiliationChanged"
+    superseded = "superseded"
+    cessation_of_operation = "cessationOfOperation"
+    certificate_hold = "certificateHold"
+    privilege_withdrawn = "privilegeWithdrawn"
+    aa_compromise = "aACompromise"
+    remove_from_crl = "removeFromCRL"
+
+
+# These are distribution point bit string mappings. Not to be confused with
+# CRLReason reason flags bit string mappings.
+# ReasonFlags ::= BIT STRING {
+#      unused                  (0),
+#      keyCompromise           (1),
+#      cACompromise            (2),
+#      affiliationChanged      (3),
+#      superseded              (4),
+#      cessationOfOperation    (5),
+#      certificateHold         (6),
+#      privilegeWithdrawn      (7),
+#      aACompromise            (8) }
+_REASON_BIT_MAPPING = {
+    1: ReasonFlags.key_compromise,
+    2: ReasonFlags.ca_compromise,
+    3: ReasonFlags.affiliation_changed,
+    4: ReasonFlags.superseded,
+    5: ReasonFlags.cessation_of_operation,
+    6: ReasonFlags.certificate_hold,
+    7: ReasonFlags.privilege_withdrawn,
+    8: ReasonFlags.aa_compromise,
+}
+
+_CRLREASONFLAGS = {
+    ReasonFlags.key_compromise: 1,
+    ReasonFlags.ca_compromise: 2,
+    ReasonFlags.affiliation_changed: 3,
+    ReasonFlags.superseded: 4,
+    ReasonFlags.cessation_of_operation: 5,
+    ReasonFlags.certificate_hold: 6,
+    ReasonFlags.privilege_withdrawn: 7,
+    ReasonFlags.aa_compromise: 8,
+}
+
+#    CRLReason ::= ENUMERATED {
+#        unspecified             (0),
+#        keyCompromise           (1),
+#        cACompromise            (2),
+#        affiliationChanged      (3),
+#        superseded              (4),
+#        cessationOfOperation    (5),
+#        certificateHold         (6),
+#             -- value 7 is not used
+#        removeFromCRL           (8),
+#        privilegeWithdrawn      (9),
+#        aACompromise           (10) }
+_CRL_ENTRY_REASON_ENUM_TO_CODE = {
+    ReasonFlags.unspecified: 0,
+    ReasonFlags.key_compromise: 1,
+    ReasonFlags.ca_compromise: 2,
+    ReasonFlags.affiliation_changed: 3,
+    ReasonFlags.superseded: 4,
+    ReasonFlags.cessation_of_operation: 5,
+    ReasonFlags.certificate_hold: 6,
+    ReasonFlags.remove_from_crl: 8,
+    ReasonFlags.privilege_withdrawn: 9,
+    ReasonFlags.aa_compromise: 10,
+}
+
+
+class PolicyConstraints(ExtensionType):
+    oid = ExtensionOID.POLICY_CONSTRAINTS
+
+    def __init__(
+        self,
+        require_explicit_policy: int | None,
+        inhibit_policy_mapping: int | None,
+    ) -> None:
+        if require_explicit_policy is not None and not isinstance(
+            require_explicit_policy, int
+        ):
+            raise TypeError(
+                "require_explicit_policy must be a non-negative integer or "
+                "None"
+            )
+
+        if inhibit_policy_mapping is not None and not isinstance(
+            inhibit_policy_mapping, int
+        ):
+            raise TypeError(
+                "inhibit_policy_mapping must be a non-negative integer or None"
+            )
+
+        if inhibit_policy_mapping is None and require_explicit_policy is None:
+            raise ValueError(
+                "At least one of require_explicit_policy and "
+                "inhibit_policy_mapping must not be None"
+            )
+
+        self._require_explicit_policy = require_explicit_policy
+        self._inhibit_policy_mapping = inhibit_policy_mapping
+
+    def __repr__(self) -> str:
+        return (
+            "<PolicyConstraints(require_explicit_policy={0.require_explicit"
+            "_policy}, inhibit_policy_mapping={0.inhibit_policy_"
+            "mapping})>".format(self)
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, PolicyConstraints):
+            return NotImplemented
+
+        return (
+            self.require_explicit_policy == other.require_explicit_policy
+            and self.inhibit_policy_mapping == other.inhibit_policy_mapping
+        )
+
+    def __hash__(self) -> int:
+        return hash(
+            (self.require_explicit_policy, self.inhibit_policy_mapping)
+        )
+
+    @property
+    def require_explicit_policy(self) -> int | None:
+        return self._require_explicit_policy
+
+    @property
+    def inhibit_policy_mapping(self) -> int | None:
+        return self._inhibit_policy_mapping
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class CertificatePolicies(ExtensionType):
+    oid = ExtensionOID.CERTIFICATE_POLICIES
+
+    def __init__(self, policies: typing.Iterable[PolicyInformation]) -> None:
+        policies = list(policies)
+        if not all(isinstance(x, PolicyInformation) for x in policies):
+            raise TypeError(
+                "Every item in the policies list must be a "
+                "PolicyInformation"
+            )
+
+        self._policies = policies
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_policies")
+
+    def __repr__(self) -> str:
+        return f"<CertificatePolicies({self._policies})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, CertificatePolicies):
+            return NotImplemented
+
+        return self._policies == other._policies
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._policies))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class PolicyInformation:
+    def __init__(
+        self,
+        policy_identifier: ObjectIdentifier,
+        policy_qualifiers: typing.Iterable[str | UserNotice] | None,
+    ) -> None:
+        if not isinstance(policy_identifier, ObjectIdentifier):
+            raise TypeError("policy_identifier must be an ObjectIdentifier")
+
+        self._policy_identifier = policy_identifier
+
+        if policy_qualifiers is not None:
+            policy_qualifiers = list(policy_qualifiers)
+            if not all(
+                isinstance(x, (str, UserNotice)) for x in policy_qualifiers
+            ):
+                raise TypeError(
+                    "policy_qualifiers must be a list of strings and/or "
+                    "UserNotice objects or None"
+                )
+
+        self._policy_qualifiers = policy_qualifiers
+
+    def __repr__(self) -> str:
+        return (
+            f"<PolicyInformation(policy_identifier={self.policy_identifier}, "
+            f"policy_qualifiers={self.policy_qualifiers})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, PolicyInformation):
+            return NotImplemented
+
+        return (
+            self.policy_identifier == other.policy_identifier
+            and self.policy_qualifiers == other.policy_qualifiers
+        )
+
+    def __hash__(self) -> int:
+        if self.policy_qualifiers is not None:
+            pq: tuple[str | UserNotice, ...] | None = tuple(
+                self.policy_qualifiers
+            )
+        else:
+            pq = None
+
+        return hash((self.policy_identifier, pq))
+
+    @property
+    def policy_identifier(self) -> ObjectIdentifier:
+        return self._policy_identifier
+
+    @property
+    def policy_qualifiers(
+        self,
+    ) -> list[str | UserNotice] | None:
+        return self._policy_qualifiers
+
+
+class UserNotice:
+    def __init__(
+        self,
+        notice_reference: NoticeReference | None,
+        explicit_text: str | None,
+    ) -> None:
+        if notice_reference and not isinstance(
+            notice_reference, NoticeReference
+        ):
+            raise TypeError(
+                "notice_reference must be None or a NoticeReference"
+            )
+
+        self._notice_reference = notice_reference
+        self._explicit_text = explicit_text
+
+    def __repr__(self) -> str:
+        return (
+            f"<UserNotice(notice_reference={self.notice_reference}, "
+            f"explicit_text={self.explicit_text!r})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, UserNotice):
+            return NotImplemented
+
+        return (
+            self.notice_reference == other.notice_reference
+            and self.explicit_text == other.explicit_text
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.notice_reference, self.explicit_text))
+
+    @property
+    def notice_reference(self) -> NoticeReference | None:
+        return self._notice_reference
+
+    @property
+    def explicit_text(self) -> str | None:
+        return self._explicit_text
+
+
+class NoticeReference:
+    def __init__(
+        self,
+        organization: str | None,
+        notice_numbers: typing.Iterable[int],
+    ) -> None:
+        self._organization = organization
+        notice_numbers = list(notice_numbers)
+        if not all(isinstance(x, int) for x in notice_numbers):
+            raise TypeError("notice_numbers must be a list of integers")
+
+        self._notice_numbers = notice_numbers
+
+    def __repr__(self) -> str:
+        return (
+            f"<NoticeReference(organization={self.organization!r}, "
+            f"notice_numbers={self.notice_numbers})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, NoticeReference):
+            return NotImplemented
+
+        return (
+            self.organization == other.organization
+            and self.notice_numbers == other.notice_numbers
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.organization, tuple(self.notice_numbers)))
+
+    @property
+    def organization(self) -> str | None:
+        return self._organization
+
+    @property
+    def notice_numbers(self) -> list[int]:
+        return self._notice_numbers
+
+
+class ExtendedKeyUsage(ExtensionType):
+    oid = ExtensionOID.EXTENDED_KEY_USAGE
+
+    def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None:
+        usages = list(usages)
+        if not all(isinstance(x, ObjectIdentifier) for x in usages):
+            raise TypeError(
+                "Every item in the usages list must be an ObjectIdentifier"
+            )
+
+        self._usages = usages
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_usages")
+
+    def __repr__(self) -> str:
+        return f"<ExtendedKeyUsage({self._usages})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, ExtendedKeyUsage):
+            return NotImplemented
+
+        return self._usages == other._usages
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._usages))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class OCSPNoCheck(ExtensionType):
+    oid = ExtensionOID.OCSP_NO_CHECK
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, OCSPNoCheck):
+            return NotImplemented
+
+        return True
+
+    def __hash__(self) -> int:
+        return hash(OCSPNoCheck)
+
+    def __repr__(self) -> str:
+        return "<OCSPNoCheck()>"
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class PrecertPoison(ExtensionType):
+    oid = ExtensionOID.PRECERT_POISON
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, PrecertPoison):
+            return NotImplemented
+
+        return True
+
+    def __hash__(self) -> int:
+        return hash(PrecertPoison)
+
+    def __repr__(self) -> str:
+        return "<PrecertPoison()>"
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class TLSFeature(ExtensionType):
+    oid = ExtensionOID.TLS_FEATURE
+
+    def __init__(self, features: typing.Iterable[TLSFeatureType]) -> None:
+        features = list(features)
+        if (
+            not all(isinstance(x, TLSFeatureType) for x in features)
+            or len(features) == 0
+        ):
+            raise TypeError(
+                "features must be a list of elements from the TLSFeatureType "
+                "enum"
+            )
+
+        self._features = features
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_features")
+
+    def __repr__(self) -> str:
+        return f"<TLSFeature(features={self._features})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, TLSFeature):
+            return NotImplemented
+
+        return self._features == other._features
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._features))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class TLSFeatureType(utils.Enum):
+    # status_request is defined in RFC 6066 and is used for what is commonly
+    # called OCSP Must-Staple when present in the TLS Feature extension in an
+    # X.509 certificate.
+    status_request = 5
+    # status_request_v2 is defined in RFC 6961 and allows multiple OCSP
+    # responses to be provided. It is not currently in use by clients or
+    # servers.
+    status_request_v2 = 17
+
+
+_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType}
+
+
+class InhibitAnyPolicy(ExtensionType):
+    oid = ExtensionOID.INHIBIT_ANY_POLICY
+
+    def __init__(self, skip_certs: int) -> None:
+        if not isinstance(skip_certs, int):
+            raise TypeError("skip_certs must be an integer")
+
+        if skip_certs < 0:
+            raise ValueError("skip_certs must be a non-negative integer")
+
+        self._skip_certs = skip_certs
+
+    def __repr__(self) -> str:
+        return f"<InhibitAnyPolicy(skip_certs={self.skip_certs})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, InhibitAnyPolicy):
+            return NotImplemented
+
+        return self.skip_certs == other.skip_certs
+
+    def __hash__(self) -> int:
+        return hash(self.skip_certs)
+
+    @property
+    def skip_certs(self) -> int:
+        return self._skip_certs
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class KeyUsage(ExtensionType):
+    oid = ExtensionOID.KEY_USAGE
+
+    def __init__(
+        self,
+        digital_signature: bool,
+        content_commitment: bool,
+        key_encipherment: bool,
+        data_encipherment: bool,
+        key_agreement: bool,
+        key_cert_sign: bool,
+        crl_sign: bool,
+        encipher_only: bool,
+        decipher_only: bool,
+    ) -> None:
+        if not key_agreement and (encipher_only or decipher_only):
+            raise ValueError(
+                "encipher_only and decipher_only can only be true when "
+                "key_agreement is true"
+            )
+
+        self._digital_signature = digital_signature
+        self._content_commitment = content_commitment
+        self._key_encipherment = key_encipherment
+        self._data_encipherment = data_encipherment
+        self._key_agreement = key_agreement
+        self._key_cert_sign = key_cert_sign
+        self._crl_sign = crl_sign
+        self._encipher_only = encipher_only
+        self._decipher_only = decipher_only
+
+    @property
+    def digital_signature(self) -> bool:
+        return self._digital_signature
+
+    @property
+    def content_commitment(self) -> bool:
+        return self._content_commitment
+
+    @property
+    def key_encipherment(self) -> bool:
+        return self._key_encipherment
+
+    @property
+    def data_encipherment(self) -> bool:
+        return self._data_encipherment
+
+    @property
+    def key_agreement(self) -> bool:
+        return self._key_agreement
+
+    @property
+    def key_cert_sign(self) -> bool:
+        return self._key_cert_sign
+
+    @property
+    def crl_sign(self) -> bool:
+        return self._crl_sign
+
+    @property
+    def encipher_only(self) -> bool:
+        if not self.key_agreement:
+            raise ValueError(
+                "encipher_only is undefined unless key_agreement is true"
+            )
+        else:
+            return self._encipher_only
+
+    @property
+    def decipher_only(self) -> bool:
+        if not self.key_agreement:
+            raise ValueError(
+                "decipher_only is undefined unless key_agreement is true"
+            )
+        else:
+            return self._decipher_only
+
+    def __repr__(self) -> str:
+        try:
+            encipher_only = self.encipher_only
+            decipher_only = self.decipher_only
+        except ValueError:
+            # Users found None confusing because even though encipher/decipher
+            # have no meaning unless key_agreement is true, to construct an
+            # instance of the class you still need to pass False.
+            encipher_only = False
+            decipher_only = False
+
+        return (
+            f"<KeyUsage(digital_signature={self.digital_signature}, "
+            f"content_commitment={self.content_commitment}, "
+            f"key_encipherment={self.key_encipherment}, "
+            f"data_encipherment={self.data_encipherment}, "
+            f"key_agreement={self.key_agreement}, "
+            f"key_cert_sign={self.key_cert_sign}, crl_sign={self.crl_sign}, "
+            f"encipher_only={encipher_only}, decipher_only={decipher_only})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, KeyUsage):
+            return NotImplemented
+
+        return (
+            self.digital_signature == other.digital_signature
+            and self.content_commitment == other.content_commitment
+            and self.key_encipherment == other.key_encipherment
+            and self.data_encipherment == other.data_encipherment
+            and self.key_agreement == other.key_agreement
+            and self.key_cert_sign == other.key_cert_sign
+            and self.crl_sign == other.crl_sign
+            and self._encipher_only == other._encipher_only
+            and self._decipher_only == other._decipher_only
+        )
+
+    def __hash__(self) -> int:
+        return hash(
+            (
+                self.digital_signature,
+                self.content_commitment,
+                self.key_encipherment,
+                self.data_encipherment,
+                self.key_agreement,
+                self.key_cert_sign,
+                self.crl_sign,
+                self._encipher_only,
+                self._decipher_only,
+            )
+        )
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class NameConstraints(ExtensionType):
+    oid = ExtensionOID.NAME_CONSTRAINTS
+
+    def __init__(
+        self,
+        permitted_subtrees: typing.Iterable[GeneralName] | None,
+        excluded_subtrees: typing.Iterable[GeneralName] | None,
+    ) -> None:
+        if permitted_subtrees is not None:
+            permitted_subtrees = list(permitted_subtrees)
+            if not permitted_subtrees:
+                raise ValueError(
+                    "permitted_subtrees must be a non-empty list or None"
+                )
+            if not all(isinstance(x, GeneralName) for x in permitted_subtrees):
+                raise TypeError(
+                    "permitted_subtrees must be a list of GeneralName objects "
+                    "or None"
+                )
+
+            self._validate_tree(permitted_subtrees)
+
+        if excluded_subtrees is not None:
+            excluded_subtrees = list(excluded_subtrees)
+            if not excluded_subtrees:
+                raise ValueError(
+                    "excluded_subtrees must be a non-empty list or None"
+                )
+            if not all(isinstance(x, GeneralName) for x in excluded_subtrees):
+                raise TypeError(
+                    "excluded_subtrees must be a list of GeneralName objects "
+                    "or None"
+                )
+
+            self._validate_tree(excluded_subtrees)
+
+        if permitted_subtrees is None and excluded_subtrees is None:
+            raise ValueError(
+                "At least one of permitted_subtrees and excluded_subtrees "
+                "must not be None"
+            )
+
+        self._permitted_subtrees = permitted_subtrees
+        self._excluded_subtrees = excluded_subtrees
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, NameConstraints):
+            return NotImplemented
+
+        return (
+            self.excluded_subtrees == other.excluded_subtrees
+            and self.permitted_subtrees == other.permitted_subtrees
+        )
+
+    def _validate_tree(self, tree: typing.Iterable[GeneralName]) -> None:
+        self._validate_ip_name(tree)
+        self._validate_dns_name(tree)
+
+    def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None:
+        if any(
+            isinstance(name, IPAddress)
+            and not isinstance(
+                name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)
+            )
+            for name in tree
+        ):
+            raise TypeError(
+                "IPAddress name constraints must be an IPv4Network or"
+                " IPv6Network object"
+            )
+
+    def _validate_dns_name(self, tree: typing.Iterable[GeneralName]) -> None:
+        if any(
+            isinstance(name, DNSName) and "*" in name.value for name in tree
+        ):
+            raise ValueError(
+                "DNSName name constraints must not contain the '*' wildcard"
+                " character"
+            )
+
+    def __repr__(self) -> str:
+        return (
+            f"<NameConstraints(permitted_subtrees={self.permitted_subtrees}, "
+            f"excluded_subtrees={self.excluded_subtrees})>"
+        )
+
+    def __hash__(self) -> int:
+        if self.permitted_subtrees is not None:
+            ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees)
+        else:
+            ps = None
+
+        if self.excluded_subtrees is not None:
+            es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees)
+        else:
+            es = None
+
+        return hash((ps, es))
+
+    @property
+    def permitted_subtrees(
+        self,
+    ) -> list[GeneralName] | None:
+        return self._permitted_subtrees
+
+    @property
+    def excluded_subtrees(
+        self,
+    ) -> list[GeneralName] | None:
+        return self._excluded_subtrees
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class Extension(typing.Generic[ExtensionTypeVar]):
+    def __init__(
+        self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar
+    ) -> None:
+        if not isinstance(oid, ObjectIdentifier):
+            raise TypeError(
+                "oid argument must be an ObjectIdentifier instance."
+            )
+
+        if not isinstance(critical, bool):
+            raise TypeError("critical must be a boolean value")
+
+        self._oid = oid
+        self._critical = critical
+        self._value = value
+
+    @property
+    def oid(self) -> ObjectIdentifier:
+        return self._oid
+
+    @property
+    def critical(self) -> bool:
+        return self._critical
+
+    @property
+    def value(self) -> ExtensionTypeVar:
+        return self._value
+
+    def __repr__(self) -> str:
+        return (
+            f"<Extension(oid={self.oid}, critical={self.critical}, "
+            f"value={self.value})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Extension):
+            return NotImplemented
+
+        return (
+            self.oid == other.oid
+            and self.critical == other.critical
+            and self.value == other.value
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.oid, self.critical, self.value))
+
+
+class GeneralNames:
+    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
+        general_names = list(general_names)
+        if not all(isinstance(x, GeneralName) for x in general_names):
+            raise TypeError(
+                "Every item in the general_names list must be an "
+                "object conforming to the GeneralName interface"
+            )
+
+        self._general_names = general_names
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[UniformResourceIdentifier]
+        | type[RFC822Name],
+    ) -> list[str]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DirectoryName],
+    ) -> list[Name]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[RegisteredID],
+    ) -> list[ObjectIdentifier]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[IPAddress]
+    ) -> list[_IPAddressTypes]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[OtherName]
+    ) -> list[OtherName]: ...
+
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[DirectoryName]
+        | type[IPAddress]
+        | type[OtherName]
+        | type[RFC822Name]
+        | type[RegisteredID]
+        | type[UniformResourceIdentifier],
+    ) -> (
+        list[_IPAddressTypes]
+        | list[str]
+        | list[OtherName]
+        | list[Name]
+        | list[ObjectIdentifier]
+    ):
+        # Return the value of each GeneralName, except for OtherName instances
+        # which we return directly because it has two important properties not
+        # just one value.
+        objs = (i for i in self if isinstance(i, type))
+        if type != OtherName:
+            return [i.value for i in objs]
+        return list(objs)
+
+    def __repr__(self) -> str:
+        return f"<GeneralNames({self._general_names})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, GeneralNames):
+            return NotImplemented
+
+        return self._general_names == other._general_names
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._general_names))
+
+
+class SubjectAlternativeName(ExtensionType):
+    oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME
+
+    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
+        self._general_names = GeneralNames(general_names)
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[UniformResourceIdentifier]
+        | type[RFC822Name],
+    ) -> list[str]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DirectoryName],
+    ) -> list[Name]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[RegisteredID],
+    ) -> list[ObjectIdentifier]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[IPAddress]
+    ) -> list[_IPAddressTypes]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[OtherName]
+    ) -> list[OtherName]: ...
+
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[DirectoryName]
+        | type[IPAddress]
+        | type[OtherName]
+        | type[RFC822Name]
+        | type[RegisteredID]
+        | type[UniformResourceIdentifier],
+    ) -> (
+        list[_IPAddressTypes]
+        | list[str]
+        | list[OtherName]
+        | list[Name]
+        | list[ObjectIdentifier]
+    ):
+        return self._general_names.get_values_for_type(type)
+
+    def __repr__(self) -> str:
+        return f"<SubjectAlternativeName({self._general_names})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SubjectAlternativeName):
+            return NotImplemented
+
+        return self._general_names == other._general_names
+
+    def __hash__(self) -> int:
+        return hash(self._general_names)
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class IssuerAlternativeName(ExtensionType):
+    oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME
+
+    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
+        self._general_names = GeneralNames(general_names)
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[UniformResourceIdentifier]
+        | type[RFC822Name],
+    ) -> list[str]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DirectoryName],
+    ) -> list[Name]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[RegisteredID],
+    ) -> list[ObjectIdentifier]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[IPAddress]
+    ) -> list[_IPAddressTypes]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[OtherName]
+    ) -> list[OtherName]: ...
+
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[DirectoryName]
+        | type[IPAddress]
+        | type[OtherName]
+        | type[RFC822Name]
+        | type[RegisteredID]
+        | type[UniformResourceIdentifier],
+    ) -> (
+        list[_IPAddressTypes]
+        | list[str]
+        | list[OtherName]
+        | list[Name]
+        | list[ObjectIdentifier]
+    ):
+        return self._general_names.get_values_for_type(type)
+
+    def __repr__(self) -> str:
+        return f"<IssuerAlternativeName({self._general_names})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, IssuerAlternativeName):
+            return NotImplemented
+
+        return self._general_names == other._general_names
+
+    def __hash__(self) -> int:
+        return hash(self._general_names)
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class CertificateIssuer(ExtensionType):
+    oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER
+
+    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
+        self._general_names = GeneralNames(general_names)
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[UniformResourceIdentifier]
+        | type[RFC822Name],
+    ) -> list[str]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[DirectoryName],
+    ) -> list[Name]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self,
+        type: type[RegisteredID],
+    ) -> list[ObjectIdentifier]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[IPAddress]
+    ) -> list[_IPAddressTypes]: ...
+
+    @typing.overload
+    def get_values_for_type(
+        self, type: type[OtherName]
+    ) -> list[OtherName]: ...
+
+    def get_values_for_type(
+        self,
+        type: type[DNSName]
+        | type[DirectoryName]
+        | type[IPAddress]
+        | type[OtherName]
+        | type[RFC822Name]
+        | type[RegisteredID]
+        | type[UniformResourceIdentifier],
+    ) -> (
+        list[_IPAddressTypes]
+        | list[str]
+        | list[OtherName]
+        | list[Name]
+        | list[ObjectIdentifier]
+    ):
+        return self._general_names.get_values_for_type(type)
+
+    def __repr__(self) -> str:
+        return f"<CertificateIssuer({self._general_names})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, CertificateIssuer):
+            return NotImplemented
+
+        return self._general_names == other._general_names
+
+    def __hash__(self) -> int:
+        return hash(self._general_names)
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class CRLReason(ExtensionType):
+    oid = CRLEntryExtensionOID.CRL_REASON
+
+    def __init__(self, reason: ReasonFlags) -> None:
+        if not isinstance(reason, ReasonFlags):
+            raise TypeError("reason must be an element from ReasonFlags")
+
+        self._reason = reason
+
+    def __repr__(self) -> str:
+        return f"<CRLReason(reason={self._reason})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, CRLReason):
+            return NotImplemented
+
+        return self.reason == other.reason
+
+    def __hash__(self) -> int:
+        return hash(self.reason)
+
+    @property
+    def reason(self) -> ReasonFlags:
+        return self._reason
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class InvalidityDate(ExtensionType):
+    oid = CRLEntryExtensionOID.INVALIDITY_DATE
+
+    def __init__(self, invalidity_date: datetime.datetime) -> None:
+        if not isinstance(invalidity_date, datetime.datetime):
+            raise TypeError("invalidity_date must be a datetime.datetime")
+
+        self._invalidity_date = invalidity_date
+
+    def __repr__(self) -> str:
+        return f"<InvalidityDate(invalidity_date={self._invalidity_date})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, InvalidityDate):
+            return NotImplemented
+
+        return self.invalidity_date == other.invalidity_date
+
+    def __hash__(self) -> int:
+        return hash(self.invalidity_date)
+
+    @property
+    def invalidity_date(self) -> datetime.datetime:
+        return self._invalidity_date
+
+    @property
+    def invalidity_date_utc(self) -> datetime.datetime:
+        if self._invalidity_date.tzinfo is None:
+            return self._invalidity_date.replace(tzinfo=datetime.timezone.utc)
+        else:
+            return self._invalidity_date.astimezone(tz=datetime.timezone.utc)
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class PrecertificateSignedCertificateTimestamps(ExtensionType):
+    oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
+
+    def __init__(
+        self,
+        signed_certificate_timestamps: typing.Iterable[
+            SignedCertificateTimestamp
+        ],
+    ) -> None:
+        signed_certificate_timestamps = list(signed_certificate_timestamps)
+        if not all(
+            isinstance(sct, SignedCertificateTimestamp)
+            for sct in signed_certificate_timestamps
+        ):
+            raise TypeError(
+                "Every item in the signed_certificate_timestamps list must be "
+                "a SignedCertificateTimestamp"
+            )
+        self._signed_certificate_timestamps = signed_certificate_timestamps
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods(
+        "_signed_certificate_timestamps"
+    )
+
+    def __repr__(self) -> str:
+        return f"<PrecertificateSignedCertificateTimestamps({list(self)})>"
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._signed_certificate_timestamps))
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, PrecertificateSignedCertificateTimestamps):
+            return NotImplemented
+
+        return (
+            self._signed_certificate_timestamps
+            == other._signed_certificate_timestamps
+        )
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class SignedCertificateTimestamps(ExtensionType):
+    oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS
+
+    def __init__(
+        self,
+        signed_certificate_timestamps: typing.Iterable[
+            SignedCertificateTimestamp
+        ],
+    ) -> None:
+        signed_certificate_timestamps = list(signed_certificate_timestamps)
+        if not all(
+            isinstance(sct, SignedCertificateTimestamp)
+            for sct in signed_certificate_timestamps
+        ):
+            raise TypeError(
+                "Every item in the signed_certificate_timestamps list must be "
+                "a SignedCertificateTimestamp"
+            )
+        self._signed_certificate_timestamps = signed_certificate_timestamps
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods(
+        "_signed_certificate_timestamps"
+    )
+
+    def __repr__(self) -> str:
+        return f"<SignedCertificateTimestamps({list(self)})>"
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._signed_certificate_timestamps))
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SignedCertificateTimestamps):
+            return NotImplemented
+
+        return (
+            self._signed_certificate_timestamps
+            == other._signed_certificate_timestamps
+        )
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class OCSPNonce(ExtensionType):
+    oid = OCSPExtensionOID.NONCE
+
+    def __init__(self, nonce: bytes) -> None:
+        if not isinstance(nonce, bytes):
+            raise TypeError("nonce must be bytes")
+
+        self._nonce = nonce
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, OCSPNonce):
+            return NotImplemented
+
+        return self.nonce == other.nonce
+
+    def __hash__(self) -> int:
+        return hash(self.nonce)
+
+    def __repr__(self) -> str:
+        return f"<OCSPNonce(nonce={self.nonce!r})>"
+
+    @property
+    def nonce(self) -> bytes:
+        return self._nonce
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class OCSPAcceptableResponses(ExtensionType):
+    oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES
+
+    def __init__(self, responses: typing.Iterable[ObjectIdentifier]) -> None:
+        responses = list(responses)
+        if any(not isinstance(r, ObjectIdentifier) for r in responses):
+            raise TypeError("All responses must be ObjectIdentifiers")
+
+        self._responses = responses
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, OCSPAcceptableResponses):
+            return NotImplemented
+
+        return self._responses == other._responses
+
+    def __hash__(self) -> int:
+        return hash(tuple(self._responses))
+
+    def __repr__(self) -> str:
+        return f"<OCSPAcceptableResponses(responses={self._responses})>"
+
+    def __iter__(self) -> typing.Iterator[ObjectIdentifier]:
+        return iter(self._responses)
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class IssuingDistributionPoint(ExtensionType):
+    oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT
+
+    def __init__(
+        self,
+        full_name: typing.Iterable[GeneralName] | None,
+        relative_name: RelativeDistinguishedName | None,
+        only_contains_user_certs: bool,
+        only_contains_ca_certs: bool,
+        only_some_reasons: frozenset[ReasonFlags] | None,
+        indirect_crl: bool,
+        only_contains_attribute_certs: bool,
+    ) -> None:
+        if full_name is not None:
+            full_name = list(full_name)
+
+        if only_some_reasons and (
+            not isinstance(only_some_reasons, frozenset)
+            or not all(isinstance(x, ReasonFlags) for x in only_some_reasons)
+        ):
+            raise TypeError(
+                "only_some_reasons must be None or frozenset of ReasonFlags"
+            )
+
+        if only_some_reasons and (
+            ReasonFlags.unspecified in only_some_reasons
+            or ReasonFlags.remove_from_crl in only_some_reasons
+        ):
+            raise ValueError(
+                "unspecified and remove_from_crl are not valid reasons in an "
+                "IssuingDistributionPoint"
+            )
+
+        if not (
+            isinstance(only_contains_user_certs, bool)
+            and isinstance(only_contains_ca_certs, bool)
+            and isinstance(indirect_crl, bool)
+            and isinstance(only_contains_attribute_certs, bool)
+        ):
+            raise TypeError(
+                "only_contains_user_certs, only_contains_ca_certs, "
+                "indirect_crl and only_contains_attribute_certs "
+                "must all be boolean."
+            )
+
+        crl_constraints = [
+            only_contains_user_certs,
+            only_contains_ca_certs,
+            indirect_crl,
+            only_contains_attribute_certs,
+        ]
+
+        if len([x for x in crl_constraints if x]) > 1:
+            raise ValueError(
+                "Only one of the following can be set to True: "
+                "only_contains_user_certs, only_contains_ca_certs, "
+                "indirect_crl, only_contains_attribute_certs"
+            )
+
+        if not any(
+            [
+                only_contains_user_certs,
+                only_contains_ca_certs,
+                indirect_crl,
+                only_contains_attribute_certs,
+                full_name,
+                relative_name,
+                only_some_reasons,
+            ]
+        ):
+            raise ValueError(
+                "Cannot create empty extension: "
+                "if only_contains_user_certs, only_contains_ca_certs, "
+                "indirect_crl, and only_contains_attribute_certs are all False"
+                ", then either full_name, relative_name, or only_some_reasons "
+                "must have a value."
+            )
+
+        self._only_contains_user_certs = only_contains_user_certs
+        self._only_contains_ca_certs = only_contains_ca_certs
+        self._indirect_crl = indirect_crl
+        self._only_contains_attribute_certs = only_contains_attribute_certs
+        self._only_some_reasons = only_some_reasons
+        self._full_name = full_name
+        self._relative_name = relative_name
+
+    def __repr__(self) -> str:
+        return (
+            f"<IssuingDistributionPoint(full_name={self.full_name}, "
+            f"relative_name={self.relative_name}, "
+            f"only_contains_user_certs={self.only_contains_user_certs}, "
+            f"only_contains_ca_certs={self.only_contains_ca_certs}, "
+            f"only_some_reasons={self.only_some_reasons}, "
+            f"indirect_crl={self.indirect_crl}, "
+            "only_contains_attribute_certs="
+            f"{self.only_contains_attribute_certs})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, IssuingDistributionPoint):
+            return NotImplemented
+
+        return (
+            self.full_name == other.full_name
+            and self.relative_name == other.relative_name
+            and self.only_contains_user_certs == other.only_contains_user_certs
+            and self.only_contains_ca_certs == other.only_contains_ca_certs
+            and self.only_some_reasons == other.only_some_reasons
+            and self.indirect_crl == other.indirect_crl
+            and self.only_contains_attribute_certs
+            == other.only_contains_attribute_certs
+        )
+
+    def __hash__(self) -> int:
+        return hash(
+            (
+                self.full_name,
+                self.relative_name,
+                self.only_contains_user_certs,
+                self.only_contains_ca_certs,
+                self.only_some_reasons,
+                self.indirect_crl,
+                self.only_contains_attribute_certs,
+            )
+        )
+
+    @property
+    def full_name(self) -> list[GeneralName] | None:
+        return self._full_name
+
+    @property
+    def relative_name(self) -> RelativeDistinguishedName | None:
+        return self._relative_name
+
+    @property
+    def only_contains_user_certs(self) -> bool:
+        return self._only_contains_user_certs
+
+    @property
+    def only_contains_ca_certs(self) -> bool:
+        return self._only_contains_ca_certs
+
+    @property
+    def only_some_reasons(
+        self,
+    ) -> frozenset[ReasonFlags] | None:
+        return self._only_some_reasons
+
+    @property
+    def indirect_crl(self) -> bool:
+        return self._indirect_crl
+
+    @property
+    def only_contains_attribute_certs(self) -> bool:
+        return self._only_contains_attribute_certs
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class MSCertificateTemplate(ExtensionType):
+    oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE
+
+    def __init__(
+        self,
+        template_id: ObjectIdentifier,
+        major_version: int | None,
+        minor_version: int | None,
+    ) -> None:
+        if not isinstance(template_id, ObjectIdentifier):
+            raise TypeError("oid must be an ObjectIdentifier")
+        self._template_id = template_id
+        if (
+            major_version is not None and not isinstance(major_version, int)
+        ) or (
+            minor_version is not None and not isinstance(minor_version, int)
+        ):
+            raise TypeError(
+                "major_version and minor_version must be integers or None"
+            )
+        self._major_version = major_version
+        self._minor_version = minor_version
+
+    @property
+    def template_id(self) -> ObjectIdentifier:
+        return self._template_id
+
+    @property
+    def major_version(self) -> int | None:
+        return self._major_version
+
+    @property
+    def minor_version(self) -> int | None:
+        return self._minor_version
+
+    def __repr__(self) -> str:
+        return (
+            f"<MSCertificateTemplate(template_id={self.template_id}, "
+            f"major_version={self.major_version}, "
+            f"minor_version={self.minor_version})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, MSCertificateTemplate):
+            return NotImplemented
+
+        return (
+            self.template_id == other.template_id
+            and self.major_version == other.major_version
+            and self.minor_version == other.minor_version
+        )
+
+    def __hash__(self) -> int:
+        return hash((self.template_id, self.major_version, self.minor_version))
+
+    def public_bytes(self) -> bytes:
+        return rust_x509.encode_extension_value(self)
+
+
+class UnrecognizedExtension(ExtensionType):
+    def __init__(self, oid: ObjectIdentifier, value: bytes) -> None:
+        if not isinstance(oid, ObjectIdentifier):
+            raise TypeError("oid must be an ObjectIdentifier")
+        self._oid = oid
+        self._value = value
+
+    @property
+    def oid(self) -> ObjectIdentifier:  # type: ignore[override]
+        return self._oid
+
+    @property
+    def value(self) -> bytes:
+        return self._value
+
+    def __repr__(self) -> str:
+        return (
+            f"<UnrecognizedExtension(oid={self.oid}, "
+            f"value={self.value!r})>"
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, UnrecognizedExtension):
+            return NotImplemented
+
+        return self.oid == other.oid and self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash((self.oid, self.value))
+
+    def public_bytes(self) -> bytes:
+        return self.value
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/general_name.py b/.venv/lib/python3.8/site-packages/cryptography/x509/general_name.py
new file mode 100644
index 0000000000000000000000000000000000000000..672f28759cb0ab6d60cb4e38a574095ae3e0159d
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/general_name.py
@@ -0,0 +1,281 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import ipaddress
+import typing
+from email.utils import parseaddr
+
+from cryptography.x509.name import Name
+from cryptography.x509.oid import ObjectIdentifier
+
+_IPAddressTypes = typing.Union[
+    ipaddress.IPv4Address,
+    ipaddress.IPv6Address,
+    ipaddress.IPv4Network,
+    ipaddress.IPv6Network,
+]
+
+
+class UnsupportedGeneralNameType(Exception):
+    pass
+
+
+class GeneralName(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def value(self) -> typing.Any:
+        """
+        Return the value of the object
+        """
+
+
+class RFC822Name(GeneralName):
+    def __init__(self, value: str) -> None:
+        if isinstance(value, str):
+            try:
+                value.encode("ascii")
+            except UnicodeEncodeError:
+                raise ValueError(
+                    "RFC822Name values should be passed as an A-label string. "
+                    "This means unicode characters should be encoded via "
+                    "a library like idna."
+                )
+        else:
+            raise TypeError("value must be string")
+
+        name, address = parseaddr(value)
+        if name or not address:
+            # parseaddr has found a name (e.g. Name <email>) or the entire
+            # value is an empty string.
+            raise ValueError("Invalid rfc822name value")
+
+        self._value = value
+
+    @property
+    def value(self) -> str:
+        return self._value
+
+    @classmethod
+    def _init_without_validation(cls, value: str) -> RFC822Name:
+        instance = cls.__new__(cls)
+        instance._value = value
+        return instance
+
+    def __repr__(self) -> str:
+        return f"<RFC822Name(value={self.value!r})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, RFC822Name):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+
+class DNSName(GeneralName):
+    def __init__(self, value: str) -> None:
+        if isinstance(value, str):
+            try:
+                value.encode("ascii")
+            except UnicodeEncodeError:
+                raise ValueError(
+                    "DNSName values should be passed as an A-label string. "
+                    "This means unicode characters should be encoded via "
+                    "a library like idna."
+                )
+        else:
+            raise TypeError("value must be string")
+
+        self._value = value
+
+    @property
+    def value(self) -> str:
+        return self._value
+
+    @classmethod
+    def _init_without_validation(cls, value: str) -> DNSName:
+        instance = cls.__new__(cls)
+        instance._value = value
+        return instance
+
+    def __repr__(self) -> str:
+        return f"<DNSName(value={self.value!r})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, DNSName):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+
+class UniformResourceIdentifier(GeneralName):
+    def __init__(self, value: str) -> None:
+        if isinstance(value, str):
+            try:
+                value.encode("ascii")
+            except UnicodeEncodeError:
+                raise ValueError(
+                    "URI values should be passed as an A-label string. "
+                    "This means unicode characters should be encoded via "
+                    "a library like idna."
+                )
+        else:
+            raise TypeError("value must be string")
+
+        self._value = value
+
+    @property
+    def value(self) -> str:
+        return self._value
+
+    @classmethod
+    def _init_without_validation(cls, value: str) -> UniformResourceIdentifier:
+        instance = cls.__new__(cls)
+        instance._value = value
+        return instance
+
+    def __repr__(self) -> str:
+        return f"<UniformResourceIdentifier(value={self.value!r})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, UniformResourceIdentifier):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+
+class DirectoryName(GeneralName):
+    def __init__(self, value: Name) -> None:
+        if not isinstance(value, Name):
+            raise TypeError("value must be a Name")
+
+        self._value = value
+
+    @property
+    def value(self) -> Name:
+        return self._value
+
+    def __repr__(self) -> str:
+        return f"<DirectoryName(value={self.value})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, DirectoryName):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+
+class RegisteredID(GeneralName):
+    def __init__(self, value: ObjectIdentifier) -> None:
+        if not isinstance(value, ObjectIdentifier):
+            raise TypeError("value must be an ObjectIdentifier")
+
+        self._value = value
+
+    @property
+    def value(self) -> ObjectIdentifier:
+        return self._value
+
+    def __repr__(self) -> str:
+        return f"<RegisteredID(value={self.value})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, RegisteredID):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+
+class IPAddress(GeneralName):
+    def __init__(self, value: _IPAddressTypes) -> None:
+        if not isinstance(
+            value,
+            (
+                ipaddress.IPv4Address,
+                ipaddress.IPv6Address,
+                ipaddress.IPv4Network,
+                ipaddress.IPv6Network,
+            ),
+        ):
+            raise TypeError(
+                "value must be an instance of ipaddress.IPv4Address, "
+                "ipaddress.IPv6Address, ipaddress.IPv4Network, or "
+                "ipaddress.IPv6Network"
+            )
+
+        self._value = value
+
+    @property
+    def value(self) -> _IPAddressTypes:
+        return self._value
+
+    def _packed(self) -> bytes:
+        if isinstance(
+            self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address)
+        ):
+            return self.value.packed
+        else:
+            return (
+                self.value.network_address.packed + self.value.netmask.packed
+            )
+
+    def __repr__(self) -> str:
+        return f"<IPAddress(value={self.value})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, IPAddress):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+
+class OtherName(GeneralName):
+    def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None:
+        if not isinstance(type_id, ObjectIdentifier):
+            raise TypeError("type_id must be an ObjectIdentifier")
+        if not isinstance(value, bytes):
+            raise TypeError("value must be a binary string")
+
+        self._type_id = type_id
+        self._value = value
+
+    @property
+    def type_id(self) -> ObjectIdentifier:
+        return self._type_id
+
+    @property
+    def value(self) -> bytes:
+        return self._value
+
+    def __repr__(self) -> str:
+        return f"<OtherName(type_id={self.type_id}, value={self.value!r})>"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, OtherName):
+            return NotImplemented
+
+        return self.type_id == other.type_id and self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash((self.type_id, self.value))
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/name.py b/.venv/lib/python3.8/site-packages/cryptography/x509/name.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b6b89d12a97c9d5d933237c65d16c55a9a87aa1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/name.py
@@ -0,0 +1,465 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import binascii
+import re
+import sys
+import typing
+import warnings
+
+from cryptography import utils
+from cryptography.hazmat.bindings._rust import x509 as rust_x509
+from cryptography.x509.oid import NameOID, ObjectIdentifier
+
+
+class _ASN1Type(utils.Enum):
+    BitString = 3
+    OctetString = 4
+    UTF8String = 12
+    NumericString = 18
+    PrintableString = 19
+    T61String = 20
+    IA5String = 22
+    UTCTime = 23
+    GeneralizedTime = 24
+    VisibleString = 26
+    UniversalString = 28
+    BMPString = 30
+
+
+_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type}
+_NAMEOID_DEFAULT_TYPE: dict[ObjectIdentifier, _ASN1Type] = {
+    NameOID.COUNTRY_NAME: _ASN1Type.PrintableString,
+    NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString,
+    NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString,
+    NameOID.DN_QUALIFIER: _ASN1Type.PrintableString,
+    NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String,
+    NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String,
+}
+
+# Type alias
+_OidNameMap = typing.Mapping[ObjectIdentifier, str]
+_NameOidMap = typing.Mapping[str, ObjectIdentifier]
+
+#: Short attribute names from RFC 4514:
+#: https://tools.ietf.org/html/rfc4514#page-7
+_NAMEOID_TO_NAME: _OidNameMap = {
+    NameOID.COMMON_NAME: "CN",
+    NameOID.LOCALITY_NAME: "L",
+    NameOID.STATE_OR_PROVINCE_NAME: "ST",
+    NameOID.ORGANIZATION_NAME: "O",
+    NameOID.ORGANIZATIONAL_UNIT_NAME: "OU",
+    NameOID.COUNTRY_NAME: "C",
+    NameOID.STREET_ADDRESS: "STREET",
+    NameOID.DOMAIN_COMPONENT: "DC",
+    NameOID.USER_ID: "UID",
+}
+_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()}
+
+_NAMEOID_LENGTH_LIMIT = {
+    NameOID.COUNTRY_NAME: (2, 2),
+    NameOID.JURISDICTION_COUNTRY_NAME: (2, 2),
+    NameOID.COMMON_NAME: (1, 64),
+}
+
+
+def _escape_dn_value(val: str | bytes) -> str:
+    """Escape special characters in RFC4514 Distinguished Name value."""
+
+    if not val:
+        return ""
+
+    # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character
+    # followed by the hexadecimal encoding of the octets.
+    if isinstance(val, bytes):
+        return "#" + binascii.hexlify(val).decode("utf8")
+
+    # See https://tools.ietf.org/html/rfc4514#section-2.4
+    val = val.replace("\\", "\\\\")
+    val = val.replace('"', '\\"')
+    val = val.replace("+", "\\+")
+    val = val.replace(",", "\\,")
+    val = val.replace(";", "\\;")
+    val = val.replace("<", "\\<")
+    val = val.replace(">", "\\>")
+    val = val.replace("\0", "\\00")
+
+    if val[0] in ("#", " "):
+        val = "\\" + val
+    if val[-1] == " ":
+        val = val[:-1] + "\\ "
+
+    return val
+
+
+def _unescape_dn_value(val: str) -> str:
+    if not val:
+        return ""
+
+    # See https://tools.ietf.org/html/rfc4514#section-3
+
+    # special = escaped / SPACE / SHARP / EQUALS
+    # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
+    def sub(m):
+        val = m.group(1)
+        # Regular escape
+        if len(val) == 1:
+            return val
+        # Hex-value scape
+        return chr(int(val, 16))
+
+    return _RFC4514NameParser._PAIR_RE.sub(sub, val)
+
+
+class NameAttribute:
+    def __init__(
+        self,
+        oid: ObjectIdentifier,
+        value: str | bytes,
+        _type: _ASN1Type | None = None,
+        *,
+        _validate: bool = True,
+    ) -> None:
+        if not isinstance(oid, ObjectIdentifier):
+            raise TypeError(
+                "oid argument must be an ObjectIdentifier instance."
+            )
+        if _type == _ASN1Type.BitString:
+            if oid != NameOID.X500_UNIQUE_IDENTIFIER:
+                raise TypeError(
+                    "oid must be X500_UNIQUE_IDENTIFIER for BitString type."
+                )
+            if not isinstance(value, bytes):
+                raise TypeError("value must be bytes for BitString")
+        else:
+            if not isinstance(value, str):
+                raise TypeError("value argument must be a str")
+
+        length_limits = _NAMEOID_LENGTH_LIMIT.get(oid)
+        if length_limits is not None:
+            min_length, max_length = length_limits
+            assert isinstance(value, str)
+            c_len = len(value.encode("utf8"))
+            if c_len < min_length or c_len > max_length:
+                msg = (
+                    f"Attribute's length must be >= {min_length} and "
+                    f"<= {max_length}, but it was {c_len}"
+                )
+                if _validate is True:
+                    raise ValueError(msg)
+                else:
+                    warnings.warn(msg, stacklevel=2)
+
+        # The appropriate ASN1 string type varies by OID and is defined across
+        # multiple RFCs including 2459, 3280, and 5280. In general UTF8String
+        # is preferred (2459), but 3280 and 5280 specify several OIDs with
+        # alternate types. This means when we see the sentinel value we need
+        # to look up whether the OID has a non-UTF8 type. If it does, set it
+        # to that. Otherwise, UTF8!
+        if _type is None:
+            _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String)
+
+        if not isinstance(_type, _ASN1Type):
+            raise TypeError("_type must be from the _ASN1Type enum")
+
+        self._oid = oid
+        self._value = value
+        self._type = _type
+
+    @property
+    def oid(self) -> ObjectIdentifier:
+        return self._oid
+
+    @property
+    def value(self) -> str | bytes:
+        return self._value
+
+    @property
+    def rfc4514_attribute_name(self) -> str:
+        """
+        The short attribute name (for example "CN") if available,
+        otherwise the OID dotted string.
+        """
+        return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string)
+
+    def rfc4514_string(
+        self, attr_name_overrides: _OidNameMap | None = None
+    ) -> str:
+        """
+        Format as RFC4514 Distinguished Name string.
+
+        Use short attribute name if available, otherwise fall back to OID
+        dotted string.
+        """
+        attr_name = (
+            attr_name_overrides.get(self.oid) if attr_name_overrides else None
+        )
+        if attr_name is None:
+            attr_name = self.rfc4514_attribute_name
+
+        return f"{attr_name}={_escape_dn_value(self.value)}"
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, NameAttribute):
+            return NotImplemented
+
+        return self.oid == other.oid and self.value == other.value
+
+    def __hash__(self) -> int:
+        return hash((self.oid, self.value))
+
+    def __repr__(self) -> str:
+        return f"<NameAttribute(oid={self.oid}, value={self.value!r})>"
+
+
+class RelativeDistinguishedName:
+    def __init__(self, attributes: typing.Iterable[NameAttribute]):
+        attributes = list(attributes)
+        if not attributes:
+            raise ValueError("a relative distinguished name cannot be empty")
+        if not all(isinstance(x, NameAttribute) for x in attributes):
+            raise TypeError("attributes must be an iterable of NameAttribute")
+
+        # Keep list and frozenset to preserve attribute order where it matters
+        self._attributes = attributes
+        self._attribute_set = frozenset(attributes)
+
+        if len(self._attribute_set) != len(attributes):
+            raise ValueError("duplicate attributes are not allowed")
+
+    def get_attributes_for_oid(
+        self, oid: ObjectIdentifier
+    ) -> list[NameAttribute]:
+        return [i for i in self if i.oid == oid]
+
+    def rfc4514_string(
+        self, attr_name_overrides: _OidNameMap | None = None
+    ) -> str:
+        """
+        Format as RFC4514 Distinguished Name string.
+
+        Within each RDN, attributes are joined by '+', although that is rarely
+        used in certificates.
+        """
+        return "+".join(
+            attr.rfc4514_string(attr_name_overrides)
+            for attr in self._attributes
+        )
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, RelativeDistinguishedName):
+            return NotImplemented
+
+        return self._attribute_set == other._attribute_set
+
+    def __hash__(self) -> int:
+        return hash(self._attribute_set)
+
+    def __iter__(self) -> typing.Iterator[NameAttribute]:
+        return iter(self._attributes)
+
+    def __len__(self) -> int:
+        return len(self._attributes)
+
+    def __repr__(self) -> str:
+        return f"<RelativeDistinguishedName({self.rfc4514_string()})>"
+
+
+class Name:
+    @typing.overload
+    def __init__(self, attributes: typing.Iterable[NameAttribute]) -> None: ...
+
+    @typing.overload
+    def __init__(
+        self, attributes: typing.Iterable[RelativeDistinguishedName]
+    ) -> None: ...
+
+    def __init__(
+        self,
+        attributes: typing.Iterable[NameAttribute | RelativeDistinguishedName],
+    ) -> None:
+        attributes = list(attributes)
+        if all(isinstance(x, NameAttribute) for x in attributes):
+            self._attributes = [
+                RelativeDistinguishedName([typing.cast(NameAttribute, x)])
+                for x in attributes
+            ]
+        elif all(isinstance(x, RelativeDistinguishedName) for x in attributes):
+            self._attributes = typing.cast(
+                typing.List[RelativeDistinguishedName], attributes
+            )
+        else:
+            raise TypeError(
+                "attributes must be a list of NameAttribute"
+                " or a list RelativeDistinguishedName"
+            )
+
+    @classmethod
+    def from_rfc4514_string(
+        cls,
+        data: str,
+        attr_name_overrides: _NameOidMap | None = None,
+    ) -> Name:
+        return _RFC4514NameParser(data, attr_name_overrides or {}).parse()
+
+    def rfc4514_string(
+        self, attr_name_overrides: _OidNameMap | None = None
+    ) -> str:
+        """
+        Format as RFC4514 Distinguished Name string.
+        For example 'CN=foobar.com,O=Foo Corp,C=US'
+
+        An X.509 name is a two-level structure: a list of sets of attributes.
+        Each list element is separated by ',' and within each list element, set
+        elements are separated by '+'. The latter is almost never used in
+        real world certificates. According to RFC4514 section 2.1 the
+        RDNSequence must be reversed when converting to string representation.
+        """
+        return ",".join(
+            attr.rfc4514_string(attr_name_overrides)
+            for attr in reversed(self._attributes)
+        )
+
+    def get_attributes_for_oid(
+        self, oid: ObjectIdentifier
+    ) -> list[NameAttribute]:
+        return [i for i in self if i.oid == oid]
+
+    @property
+    def rdns(self) -> list[RelativeDistinguishedName]:
+        return self._attributes
+
+    def public_bytes(self, backend: typing.Any = None) -> bytes:
+        return rust_x509.encode_name_bytes(self)
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Name):
+            return NotImplemented
+
+        return self._attributes == other._attributes
+
+    def __hash__(self) -> int:
+        # TODO: this is relatively expensive, if this looks like a bottleneck
+        # for you, consider optimizing!
+        return hash(tuple(self._attributes))
+
+    def __iter__(self) -> typing.Iterator[NameAttribute]:
+        for rdn in self._attributes:
+            yield from rdn
+
+    def __len__(self) -> int:
+        return sum(len(rdn) for rdn in self._attributes)
+
+    def __repr__(self) -> str:
+        rdns = ",".join(attr.rfc4514_string() for attr in self._attributes)
+        return f"<Name({rdns})>"
+
+
+class _RFC4514NameParser:
+    _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+")
+    _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*")
+
+    _PAIR = r"\\([\\ #=\"\+,;<>]|[\da-zA-Z]{2})"
+    _PAIR_RE = re.compile(_PAIR)
+    _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
+    _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
+    _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
+    _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]"
+    _LEADCHAR = rf"{_LUTF1}|{_UTFMB}"
+    _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}"
+    _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}"
+    _STRING_RE = re.compile(
+        rf"""
+        (
+            ({_LEADCHAR}|{_PAIR})
+            (
+                ({_STRINGCHAR}|{_PAIR})*
+                ({_TRAILCHAR}|{_PAIR})
+            )?
+        )?
+        """,
+        re.VERBOSE,
+    )
+    _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+")
+
+    def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None:
+        self._data = data
+        self._idx = 0
+
+        self._attr_name_overrides = attr_name_overrides
+
+    def _has_data(self) -> bool:
+        return self._idx < len(self._data)
+
+    def _peek(self) -> str | None:
+        if self._has_data():
+            return self._data[self._idx]
+        return None
+
+    def _read_char(self, ch: str) -> None:
+        if self._peek() != ch:
+            raise ValueError
+        self._idx += 1
+
+    def _read_re(self, pat) -> str:
+        match = pat.match(self._data, pos=self._idx)
+        if match is None:
+            raise ValueError
+        val = match.group()
+        self._idx += len(val)
+        return val
+
+    def parse(self) -> Name:
+        """
+        Parses the `data` string and converts it to a Name.
+
+        According to RFC4514 section 2.1 the RDNSequence must be
+        reversed when converting to string representation. So, when
+        we parse it, we need to reverse again to get the RDNs on the
+        correct order.
+        """
+
+        if not self._has_data():
+            return Name([])
+
+        rdns = [self._parse_rdn()]
+
+        while self._has_data():
+            self._read_char(",")
+            rdns.append(self._parse_rdn())
+
+        return Name(reversed(rdns))
+
+    def _parse_rdn(self) -> RelativeDistinguishedName:
+        nas = [self._parse_na()]
+        while self._peek() == "+":
+            self._read_char("+")
+            nas.append(self._parse_na())
+
+        return RelativeDistinguishedName(nas)
+
+    def _parse_na(self) -> NameAttribute:
+        try:
+            oid_value = self._read_re(self._OID_RE)
+        except ValueError:
+            name = self._read_re(self._DESCR_RE)
+            oid = self._attr_name_overrides.get(
+                name, _NAME_TO_NAMEOID.get(name)
+            )
+            if oid is None:
+                raise ValueError
+        else:
+            oid = ObjectIdentifier(oid_value)
+
+        self._read_char("=")
+        if self._peek() == "#":
+            value = self._read_re(self._HEXSTRING_RE)
+            value = binascii.unhexlify(value[1:]).decode()
+        else:
+            raw_value = self._read_re(self._STRING_RE)
+            value = _unescape_dn_value(raw_value)
+
+        return NameAttribute(oid, value)
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/ocsp.py b/.venv/lib/python3.8/site-packages/cryptography/x509/ocsp.py
new file mode 100644
index 0000000000000000000000000000000000000000..dbb475db2ab2930a583b1d2a61f411b719ef1b8b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/ocsp.py
@@ -0,0 +1,678 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import datetime
+import typing
+
+from cryptography import utils, x509
+from cryptography.hazmat.bindings._rust import ocsp
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric.types import (
+    CertificateIssuerPrivateKeyTypes,
+)
+from cryptography.x509.base import (
+    _EARLIEST_UTC_TIME,
+    _convert_to_naive_utc_time,
+    _reject_duplicate_extension,
+)
+
+
+class OCSPResponderEncoding(utils.Enum):
+    HASH = "By Hash"
+    NAME = "By Name"
+
+
+class OCSPResponseStatus(utils.Enum):
+    SUCCESSFUL = 0
+    MALFORMED_REQUEST = 1
+    INTERNAL_ERROR = 2
+    TRY_LATER = 3
+    SIG_REQUIRED = 5
+    UNAUTHORIZED = 6
+
+
+_ALLOWED_HASHES = (
+    hashes.SHA1,
+    hashes.SHA224,
+    hashes.SHA256,
+    hashes.SHA384,
+    hashes.SHA512,
+)
+
+
+def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None:
+    if not isinstance(algorithm, _ALLOWED_HASHES):
+        raise ValueError(
+            "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"
+        )
+
+
+class OCSPCertStatus(utils.Enum):
+    GOOD = 0
+    REVOKED = 1
+    UNKNOWN = 2
+
+
+class _SingleResponse:
+    def __init__(
+        self,
+        cert: x509.Certificate,
+        issuer: x509.Certificate,
+        algorithm: hashes.HashAlgorithm,
+        cert_status: OCSPCertStatus,
+        this_update: datetime.datetime,
+        next_update: datetime.datetime | None,
+        revocation_time: datetime.datetime | None,
+        revocation_reason: x509.ReasonFlags | None,
+    ):
+        if not isinstance(cert, x509.Certificate) or not isinstance(
+            issuer, x509.Certificate
+        ):
+            raise TypeError("cert and issuer must be a Certificate")
+
+        _verify_algorithm(algorithm)
+        if not isinstance(this_update, datetime.datetime):
+            raise TypeError("this_update must be a datetime object")
+        if next_update is not None and not isinstance(
+            next_update, datetime.datetime
+        ):
+            raise TypeError("next_update must be a datetime object or None")
+
+        self._cert = cert
+        self._issuer = issuer
+        self._algorithm = algorithm
+        self._this_update = this_update
+        self._next_update = next_update
+
+        if not isinstance(cert_status, OCSPCertStatus):
+            raise TypeError(
+                "cert_status must be an item from the OCSPCertStatus enum"
+            )
+        if cert_status is not OCSPCertStatus.REVOKED:
+            if revocation_time is not None:
+                raise ValueError(
+                    "revocation_time can only be provided if the certificate "
+                    "is revoked"
+                )
+            if revocation_reason is not None:
+                raise ValueError(
+                    "revocation_reason can only be provided if the certificate"
+                    " is revoked"
+                )
+        else:
+            if not isinstance(revocation_time, datetime.datetime):
+                raise TypeError("revocation_time must be a datetime object")
+
+            revocation_time = _convert_to_naive_utc_time(revocation_time)
+            if revocation_time < _EARLIEST_UTC_TIME:
+                raise ValueError(
+                    "The revocation_time must be on or after"
+                    " 1950 January 1."
+                )
+
+            if revocation_reason is not None and not isinstance(
+                revocation_reason, x509.ReasonFlags
+            ):
+                raise TypeError(
+                    "revocation_reason must be an item from the ReasonFlags "
+                    "enum or None"
+                )
+
+        self._cert_status = cert_status
+        self._revocation_time = revocation_time
+        self._revocation_reason = revocation_reason
+
+
+class OCSPRequest(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def issuer_key_hash(self) -> bytes:
+        """
+        The hash of the issuer public key
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer_name_hash(self) -> bytes:
+        """
+        The hash of the issuer name
+        """
+
+    @property
+    @abc.abstractmethod
+    def hash_algorithm(self) -> hashes.HashAlgorithm:
+        """
+        The hash algorithm used in the issuer name and key hashes
+        """
+
+    @property
+    @abc.abstractmethod
+    def serial_number(self) -> int:
+        """
+        The serial number of the cert whose status is being checked
+        """
+
+    @abc.abstractmethod
+    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
+        """
+        Serializes the request to DER
+        """
+
+    @property
+    @abc.abstractmethod
+    def extensions(self) -> x509.Extensions:
+        """
+        The list of request extensions. Not single request extensions.
+        """
+
+
+class OCSPSingleResponse(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def certificate_status(self) -> OCSPCertStatus:
+        """
+        The status of the certificate (an element from the OCSPCertStatus enum)
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_time(self) -> datetime.datetime | None:
+        """
+        The date of when the certificate was revoked or None if not
+        revoked.
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_time_utc(self) -> datetime.datetime | None:
+        """
+        The date of when the certificate was revoked or None if not
+        revoked. Represented as a non-naive UTC datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_reason(self) -> x509.ReasonFlags | None:
+        """
+        The reason the certificate was revoked or None if not specified or
+        not revoked.
+        """
+
+    @property
+    @abc.abstractmethod
+    def this_update(self) -> datetime.datetime:
+        """
+        The most recent time at which the status being indicated is known by
+        the responder to have been correct
+        """
+
+    @property
+    @abc.abstractmethod
+    def this_update_utc(self) -> datetime.datetime:
+        """
+        The most recent time at which the status being indicated is known by
+        the responder to have been correct. Represented as a non-naive UTC
+        datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def next_update(self) -> datetime.datetime | None:
+        """
+        The time when newer information will be available
+        """
+
+    @property
+    @abc.abstractmethod
+    def next_update_utc(self) -> datetime.datetime | None:
+        """
+        The time when newer information will be available. Represented as a
+        non-naive UTC datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer_key_hash(self) -> bytes:
+        """
+        The hash of the issuer public key
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer_name_hash(self) -> bytes:
+        """
+        The hash of the issuer name
+        """
+
+    @property
+    @abc.abstractmethod
+    def hash_algorithm(self) -> hashes.HashAlgorithm:
+        """
+        The hash algorithm used in the issuer name and key hashes
+        """
+
+    @property
+    @abc.abstractmethod
+    def serial_number(self) -> int:
+        """
+        The serial number of the cert whose status is being checked
+        """
+
+
+class OCSPResponse(metaclass=abc.ABCMeta):
+    @property
+    @abc.abstractmethod
+    def responses(self) -> typing.Iterator[OCSPSingleResponse]:
+        """
+        An iterator over the individual SINGLERESP structures in the
+        response
+        """
+
+    @property
+    @abc.abstractmethod
+    def response_status(self) -> OCSPResponseStatus:
+        """
+        The status of the response. This is a value from the OCSPResponseStatus
+        enumeration
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_algorithm_oid(self) -> x509.ObjectIdentifier:
+        """
+        The ObjectIdentifier of the signature algorithm
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature_hash_algorithm(
+        self,
+    ) -> hashes.HashAlgorithm | None:
+        """
+        Returns a HashAlgorithm corresponding to the type of the digest signed
+        """
+
+    @property
+    @abc.abstractmethod
+    def signature(self) -> bytes:
+        """
+        The signature bytes
+        """
+
+    @property
+    @abc.abstractmethod
+    def tbs_response_bytes(self) -> bytes:
+        """
+        The tbsResponseData bytes
+        """
+
+    @property
+    @abc.abstractmethod
+    def certificates(self) -> list[x509.Certificate]:
+        """
+        A list of certificates used to help build a chain to verify the OCSP
+        response. This situation occurs when the OCSP responder uses a delegate
+        certificate.
+        """
+
+    @property
+    @abc.abstractmethod
+    def responder_key_hash(self) -> bytes | None:
+        """
+        The responder's key hash or None
+        """
+
+    @property
+    @abc.abstractmethod
+    def responder_name(self) -> x509.Name | None:
+        """
+        The responder's Name or None
+        """
+
+    @property
+    @abc.abstractmethod
+    def produced_at(self) -> datetime.datetime:
+        """
+        The time the response was produced
+        """
+
+    @property
+    @abc.abstractmethod
+    def produced_at_utc(self) -> datetime.datetime:
+        """
+        The time the response was produced. Represented as a non-naive UTC
+        datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def certificate_status(self) -> OCSPCertStatus:
+        """
+        The status of the certificate (an element from the OCSPCertStatus enum)
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_time(self) -> datetime.datetime | None:
+        """
+        The date of when the certificate was revoked or None if not
+        revoked.
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_time_utc(self) -> datetime.datetime | None:
+        """
+        The date of when the certificate was revoked or None if not
+        revoked. Represented as a non-naive UTC datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def revocation_reason(self) -> x509.ReasonFlags | None:
+        """
+        The reason the certificate was revoked or None if not specified or
+        not revoked.
+        """
+
+    @property
+    @abc.abstractmethod
+    def this_update(self) -> datetime.datetime:
+        """
+        The most recent time at which the status being indicated is known by
+        the responder to have been correct
+        """
+
+    @property
+    @abc.abstractmethod
+    def this_update_utc(self) -> datetime.datetime:
+        """
+        The most recent time at which the status being indicated is known by
+        the responder to have been correct. Represented as a non-naive UTC
+        datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def next_update(self) -> datetime.datetime | None:
+        """
+        The time when newer information will be available
+        """
+
+    @property
+    @abc.abstractmethod
+    def next_update_utc(self) -> datetime.datetime | None:
+        """
+        The time when newer information will be available. Represented as a
+        non-naive UTC datetime.
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer_key_hash(self) -> bytes:
+        """
+        The hash of the issuer public key
+        """
+
+    @property
+    @abc.abstractmethod
+    def issuer_name_hash(self) -> bytes:
+        """
+        The hash of the issuer name
+        """
+
+    @property
+    @abc.abstractmethod
+    def hash_algorithm(self) -> hashes.HashAlgorithm:
+        """
+        The hash algorithm used in the issuer name and key hashes
+        """
+
+    @property
+    @abc.abstractmethod
+    def serial_number(self) -> int:
+        """
+        The serial number of the cert whose status is being checked
+        """
+
+    @property
+    @abc.abstractmethod
+    def extensions(self) -> x509.Extensions:
+        """
+        The list of response extensions. Not single response extensions.
+        """
+
+    @property
+    @abc.abstractmethod
+    def single_extensions(self) -> x509.Extensions:
+        """
+        The list of single response extensions. Not response extensions.
+        """
+
+    @abc.abstractmethod
+    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
+        """
+        Serializes the response to DER
+        """
+
+
+OCSPRequest.register(ocsp.OCSPRequest)
+OCSPResponse.register(ocsp.OCSPResponse)
+OCSPSingleResponse.register(ocsp.OCSPSingleResponse)
+
+
+class OCSPRequestBuilder:
+    def __init__(
+        self,
+        request: tuple[
+            x509.Certificate, x509.Certificate, hashes.HashAlgorithm
+        ]
+        | None = None,
+        request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm]
+        | None = None,
+        extensions: list[x509.Extension[x509.ExtensionType]] = [],
+    ) -> None:
+        self._request = request
+        self._request_hash = request_hash
+        self._extensions = extensions
+
+    def add_certificate(
+        self,
+        cert: x509.Certificate,
+        issuer: x509.Certificate,
+        algorithm: hashes.HashAlgorithm,
+    ) -> OCSPRequestBuilder:
+        if self._request is not None or self._request_hash is not None:
+            raise ValueError("Only one certificate can be added to a request")
+
+        _verify_algorithm(algorithm)
+        if not isinstance(cert, x509.Certificate) or not isinstance(
+            issuer, x509.Certificate
+        ):
+            raise TypeError("cert and issuer must be a Certificate")
+
+        return OCSPRequestBuilder(
+            (cert, issuer, algorithm), self._request_hash, self._extensions
+        )
+
+    def add_certificate_by_hash(
+        self,
+        issuer_name_hash: bytes,
+        issuer_key_hash: bytes,
+        serial_number: int,
+        algorithm: hashes.HashAlgorithm,
+    ) -> OCSPRequestBuilder:
+        if self._request is not None or self._request_hash is not None:
+            raise ValueError("Only one certificate can be added to a request")
+
+        if not isinstance(serial_number, int):
+            raise TypeError("serial_number must be an integer")
+
+        _verify_algorithm(algorithm)
+        utils._check_bytes("issuer_name_hash", issuer_name_hash)
+        utils._check_bytes("issuer_key_hash", issuer_key_hash)
+        if algorithm.digest_size != len(
+            issuer_name_hash
+        ) or algorithm.digest_size != len(issuer_key_hash):
+            raise ValueError(
+                "issuer_name_hash and issuer_key_hash must be the same length "
+                "as the digest size of the algorithm"
+            )
+
+        return OCSPRequestBuilder(
+            self._request,
+            (issuer_name_hash, issuer_key_hash, serial_number, algorithm),
+            self._extensions,
+        )
+
+    def add_extension(
+        self, extval: x509.ExtensionType, critical: bool
+    ) -> OCSPRequestBuilder:
+        if not isinstance(extval, x509.ExtensionType):
+            raise TypeError("extension must be an ExtensionType")
+
+        extension = x509.Extension(extval.oid, critical, extval)
+        _reject_duplicate_extension(extension, self._extensions)
+
+        return OCSPRequestBuilder(
+            self._request, self._request_hash, [*self._extensions, extension]
+        )
+
+    def build(self) -> OCSPRequest:
+        if self._request is None and self._request_hash is None:
+            raise ValueError("You must add a certificate before building")
+
+        return ocsp.create_ocsp_request(self)
+
+
+class OCSPResponseBuilder:
+    def __init__(
+        self,
+        response: _SingleResponse | None = None,
+        responder_id: tuple[x509.Certificate, OCSPResponderEncoding]
+        | None = None,
+        certs: list[x509.Certificate] | None = None,
+        extensions: list[x509.Extension[x509.ExtensionType]] = [],
+    ):
+        self._response = response
+        self._responder_id = responder_id
+        self._certs = certs
+        self._extensions = extensions
+
+    def add_response(
+        self,
+        cert: x509.Certificate,
+        issuer: x509.Certificate,
+        algorithm: hashes.HashAlgorithm,
+        cert_status: OCSPCertStatus,
+        this_update: datetime.datetime,
+        next_update: datetime.datetime | None,
+        revocation_time: datetime.datetime | None,
+        revocation_reason: x509.ReasonFlags | None,
+    ) -> OCSPResponseBuilder:
+        if self._response is not None:
+            raise ValueError("Only one response per OCSPResponse.")
+
+        singleresp = _SingleResponse(
+            cert,
+            issuer,
+            algorithm,
+            cert_status,
+            this_update,
+            next_update,
+            revocation_time,
+            revocation_reason,
+        )
+        return OCSPResponseBuilder(
+            singleresp,
+            self._responder_id,
+            self._certs,
+            self._extensions,
+        )
+
+    def responder_id(
+        self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate
+    ) -> OCSPResponseBuilder:
+        if self._responder_id is not None:
+            raise ValueError("responder_id can only be set once")
+        if not isinstance(responder_cert, x509.Certificate):
+            raise TypeError("responder_cert must be a Certificate")
+        if not isinstance(encoding, OCSPResponderEncoding):
+            raise TypeError(
+                "encoding must be an element from OCSPResponderEncoding"
+            )
+
+        return OCSPResponseBuilder(
+            self._response,
+            (responder_cert, encoding),
+            self._certs,
+            self._extensions,
+        )
+
+    def certificates(
+        self, certs: typing.Iterable[x509.Certificate]
+    ) -> OCSPResponseBuilder:
+        if self._certs is not None:
+            raise ValueError("certificates may only be set once")
+        certs = list(certs)
+        if len(certs) == 0:
+            raise ValueError("certs must not be an empty list")
+        if not all(isinstance(x, x509.Certificate) for x in certs):
+            raise TypeError("certs must be a list of Certificates")
+        return OCSPResponseBuilder(
+            self._response,
+            self._responder_id,
+            certs,
+            self._extensions,
+        )
+
+    def add_extension(
+        self, extval: x509.ExtensionType, critical: bool
+    ) -> OCSPResponseBuilder:
+        if not isinstance(extval, x509.ExtensionType):
+            raise TypeError("extension must be an ExtensionType")
+
+        extension = x509.Extension(extval.oid, critical, extval)
+        _reject_duplicate_extension(extension, self._extensions)
+
+        return OCSPResponseBuilder(
+            self._response,
+            self._responder_id,
+            self._certs,
+            [*self._extensions, extension],
+        )
+
+    def sign(
+        self,
+        private_key: CertificateIssuerPrivateKeyTypes,
+        algorithm: hashes.HashAlgorithm | None,
+    ) -> OCSPResponse:
+        if self._response is None:
+            raise ValueError("You must add a response before signing")
+        if self._responder_id is None:
+            raise ValueError("You must add a responder_id before signing")
+
+        return ocsp.create_ocsp_response(
+            OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm
+        )
+
+    @classmethod
+    def build_unsuccessful(
+        cls, response_status: OCSPResponseStatus
+    ) -> OCSPResponse:
+        if not isinstance(response_status, OCSPResponseStatus):
+            raise TypeError(
+                "response_status must be an item from OCSPResponseStatus"
+            )
+        if response_status is OCSPResponseStatus.SUCCESSFUL:
+            raise ValueError("response_status cannot be SUCCESSFUL")
+
+        return ocsp.create_ocsp_response(response_status, None, None, None)
+
+
+load_der_ocsp_request = ocsp.load_der_ocsp_request
+load_der_ocsp_response = ocsp.load_der_ocsp_response
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/oid.py b/.venv/lib/python3.8/site-packages/cryptography/x509/oid.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4e409e0a2a0132054614f16310bf045d3874ed0
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/oid.py
@@ -0,0 +1,35 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+from cryptography.hazmat._oid import (
+    AttributeOID,
+    AuthorityInformationAccessOID,
+    CertificatePoliciesOID,
+    CRLEntryExtensionOID,
+    ExtendedKeyUsageOID,
+    ExtensionOID,
+    NameOID,
+    ObjectIdentifier,
+    OCSPExtensionOID,
+    PublicKeyAlgorithmOID,
+    SignatureAlgorithmOID,
+    SubjectInformationAccessOID,
+)
+
+__all__ = [
+    "AttributeOID",
+    "AuthorityInformationAccessOID",
+    "CRLEntryExtensionOID",
+    "CertificatePoliciesOID",
+    "ExtendedKeyUsageOID",
+    "ExtensionOID",
+    "NameOID",
+    "OCSPExtensionOID",
+    "ObjectIdentifier",
+    "PublicKeyAlgorithmOID",
+    "SignatureAlgorithmOID",
+    "SubjectInformationAccessOID",
+]
diff --git a/.venv/lib/python3.8/site-packages/cryptography/x509/verification.py b/.venv/lib/python3.8/site-packages/cryptography/x509/verification.py
new file mode 100644
index 0000000000000000000000000000000000000000..b83650681237be2cfb8fc5d24f1d1f2c5782c2f5
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/cryptography/x509/verification.py
@@ -0,0 +1,28 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import typing
+
+from cryptography.hazmat.bindings._rust import x509 as rust_x509
+from cryptography.x509.general_name import DNSName, IPAddress
+
+__all__ = [
+    "ClientVerifier",
+    "PolicyBuilder",
+    "ServerVerifier",
+    "Store",
+    "Subject",
+    "VerificationError",
+    "VerifiedClient",
+]
+
+Store = rust_x509.Store
+Subject = typing.Union[DNSName, IPAddress]
+VerifiedClient = rust_x509.VerifiedClient
+ClientVerifier = rust_x509.ClientVerifier
+ServerVerifier = rust_x509.ServerVerifier
+PolicyBuilder = rust_x509.PolicyBuilder
+VerificationError = rust_x509.VerificationError
diff --git a/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/INSTALLER b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/LICENSE b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..bee14a47d2d83f2d4e3aca6d5a41e587ebf3c516
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/LICENSE
@@ -0,0 +1,27 @@
+pycparser -- A C parser in Python
+
+Copyright (c) 2008-2022, Eli Bendersky
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this 
+  list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, 
+  this list of conditions and the following disclaimer in the documentation 
+  and/or other materials provided with the distribution.
+* Neither the name of the copyright holder nor the names of its contributors may 
+  be used to endorse or promote products derived from this software without 
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/METADATA b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..2c8038a3c2362879d22d50c54b1ff0cc479990d3
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/METADATA
@@ -0,0 +1,28 @@
+Metadata-Version: 2.1
+Name: pycparser
+Version: 2.22
+Summary: C parser in Python
+Home-page: https://github.com/eliben/pycparser
+Author: Eli Bendersky
+Author-email: eliben@gmail.com
+Maintainer: Eli Bendersky
+License: BSD-3-Clause
+Platform: Cross Platform
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Requires-Python: >=3.8
+License-File: LICENSE
+
+
+        pycparser is a complete parser of the C language, written in
+        pure Python using the PLY parsing library.
+        It parses C code into an AST and can serve as a front-end for
+        C compilers or analysis tools.
+    
+
diff --git a/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/RECORD b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..850515a31c777b51a34f4f84330f7f17c0768a47
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/RECORD
@@ -0,0 +1,41 @@
+pycparser-2.22.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pycparser-2.22.dist-info/LICENSE,sha256=DIRjmTaep23de1xE_m0WSXQV_PAV9cu1CMJL-YuBxbE,1543
+pycparser-2.22.dist-info/METADATA,sha256=3XOB8nggH4ijl17DCjUhk7g6qioMJLprUlEkwYgZvW8,943
+pycparser-2.22.dist-info/RECORD,,
+pycparser-2.22.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+pycparser-2.22.dist-info/top_level.txt,sha256=c-lPcS74L_8KoH7IE6PQF5ofyirRQNV4VhkbSFIPeWM,10
+pycparser/__init__.py,sha256=hrf-AyuVYNHQGTD0Nv2bywxoTN3N1ZCs03m-9-QDS14,2918
+pycparser/__pycache__/__init__.cpython-38.pyc,,
+pycparser/__pycache__/_ast_gen.cpython-38.pyc,,
+pycparser/__pycache__/_build_tables.cpython-38.pyc,,
+pycparser/__pycache__/ast_transforms.cpython-38.pyc,,
+pycparser/__pycache__/c_ast.cpython-38.pyc,,
+pycparser/__pycache__/c_generator.cpython-38.pyc,,
+pycparser/__pycache__/c_lexer.cpython-38.pyc,,
+pycparser/__pycache__/c_parser.cpython-38.pyc,,
+pycparser/__pycache__/lextab.cpython-38.pyc,,
+pycparser/__pycache__/plyparser.cpython-38.pyc,,
+pycparser/__pycache__/yacctab.cpython-38.pyc,,
+pycparser/_ast_gen.py,sha256=0JRVnDW-Jw-3IjVlo8je9rbAcp6Ko7toHAnB5zi7h0Q,10555
+pycparser/_build_tables.py,sha256=4d_UkIxJ4YfHTVn6xBzBA52wDo7qxg1B6aZAJYJas9Q,1087
+pycparser/_c_ast.cfg,sha256=ld5ezE9yzIJFIVAUfw7ezJSlMi4nXKNCzfmqjOyQTNo,4255
+pycparser/ast_transforms.py,sha256=GTMYlUgWmXd5wJVyovXY1qzzAqjxzCpVVg0664dKGBs,5691
+pycparser/c_ast.py,sha256=HWeOrfYdCY0u5XaYhE1i60uVyE3yMWdcxzECUX-DqJw,31445
+pycparser/c_generator.py,sha256=yi6Mcqxv88J5ue8k5-mVGxh3iJ37iD4QyF-sWcGjC-8,17772
+pycparser/c_lexer.py,sha256=RSUjq0SRH8dkvwrQslBIZY2AXOrpQpe-oO1udJXotZk,17186
+pycparser/c_parser.py,sha256=WUnIHNydl32QBuRUqrqk-F2lyB6WRP4BUYFELqVETyw,74282
+pycparser/lextab.py,sha256=Nc3I0_D8Xlf-BOpfOKkEvFw-rPuFPPwAjkcLubwTCU4,8554
+pycparser/ply/__init__.py,sha256=q4s86QwRsYRa20L9ueSxfh-hPihpftBjDOvYa2_SS2Y,102
+pycparser/ply/__pycache__/__init__.cpython-38.pyc,,
+pycparser/ply/__pycache__/cpp.cpython-38.pyc,,
+pycparser/ply/__pycache__/ctokens.cpython-38.pyc,,
+pycparser/ply/__pycache__/lex.cpython-38.pyc,,
+pycparser/ply/__pycache__/yacc.cpython-38.pyc,,
+pycparser/ply/__pycache__/ygen.cpython-38.pyc,,
+pycparser/ply/cpp.py,sha256=UtC3ylTWp5_1MKA-PLCuwKQR8zSOnlGuGGIdzj8xS98,33282
+pycparser/ply/ctokens.py,sha256=MKksnN40TehPhgVfxCJhjj_BjL943apreABKYz-bl0Y,3177
+pycparser/ply/lex.py,sha256=rCMi0yjlZmjH5SNXj_Yds1VxSDkaG2thS7351YvfN-I,42926
+pycparser/ply/yacc.py,sha256=eatSDkRLgRr6X3-hoDk_SQQv065R0BdL2K7fQ54CgVM,137323
+pycparser/ply/ygen.py,sha256=2JYNeYtrPz1JzLSLO3d4GsS8zJU8jY_I_CR1VI9gWrA,2251
+pycparser/plyparser.py,sha256=8tLOoEytcapvWrr1JfCf7Dog-wulBtS1YrDs8S7JfMo,4875
+pycparser/yacctab.py,sha256=B6ck8QEPnRi04VSxKEL6xHaP8sEEsTbWtwsjfKHABgM,209738
diff --git a/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/WHEEL b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..becc9a66ea739ba941d48a749e248761cc6e658a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/top_level.txt b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dc1c9e101ad9ccd943b359338ef42c342ebc84a1
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser-2.22.dist-info/top_level.txt
@@ -0,0 +1 @@
+pycparser
diff --git a/.venv/lib/python3.8/site-packages/pycparser/__init__.py b/.venv/lib/python3.8/site-packages/pycparser/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf4b0d411dbe5e91780553753af9c77f737bbd59
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/__init__.py
@@ -0,0 +1,93 @@
+#-----------------------------------------------------------------
+# pycparser: __init__.py
+#
+# This package file exports some convenience functions for
+# interacting with pycparser
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+__all__ = ['c_lexer', 'c_parser', 'c_ast']
+__version__ = '2.22'
+
+import io
+from subprocess import check_output
+from .c_parser import CParser
+
+
+def preprocess_file(filename, cpp_path='cpp', cpp_args=''):
+    """ Preprocess a file using cpp.
+
+        filename:
+            Name of the file you want to preprocess.
+
+        cpp_path:
+        cpp_args:
+            Refer to the documentation of parse_file for the meaning of these
+            arguments.
+
+        When successful, returns the preprocessed file's contents.
+        Errors from cpp will be printed out.
+    """
+    path_list = [cpp_path]
+    if isinstance(cpp_args, list):
+        path_list += cpp_args
+    elif cpp_args != '':
+        path_list += [cpp_args]
+    path_list += [filename]
+
+    try:
+        # Note the use of universal_newlines to treat all newlines
+        # as \n for Python's purpose
+        text = check_output(path_list, universal_newlines=True)
+    except OSError as e:
+        raise RuntimeError("Unable to invoke 'cpp'.  " +
+            'Make sure its path was passed correctly\n' +
+            ('Original error: %s' % e))
+
+    return text
+
+
+def parse_file(filename, use_cpp=False, cpp_path='cpp', cpp_args='',
+               parser=None, encoding=None):
+    """ Parse a C file using pycparser.
+
+        filename:
+            Name of the file you want to parse.
+
+        use_cpp:
+            Set to True if you want to execute the C pre-processor
+            on the file prior to parsing it.
+
+        cpp_path:
+            If use_cpp is True, this is the path to 'cpp' on your
+            system. If no path is provided, it attempts to just
+            execute 'cpp', so it must be in your PATH.
+
+        cpp_args:
+            If use_cpp is True, set this to the command line arguments strings
+            to cpp. Be careful with quotes - it's best to pass a raw string
+            (r'') here. For example:
+            r'-I../utils/fake_libc_include'
+            If several arguments are required, pass a list of strings.
+
+        encoding:
+            Encoding to use for the file to parse
+
+        parser:
+            Optional parser object to be used instead of the default CParser
+
+        When successful, an AST is returned. ParseError can be
+        thrown if the file doesn't parse successfully.
+
+        Errors from cpp will be printed out.
+    """
+    if use_cpp:
+        text = preprocess_file(filename, cpp_path, cpp_args)
+    else:
+        with io.open(filename, encoding=encoding) as f:
+            text = f.read()
+
+    if parser is None:
+        parser = CParser()
+    return parser.parse(text, filename)
diff --git a/.venv/lib/python3.8/site-packages/pycparser/_ast_gen.py b/.venv/lib/python3.8/site-packages/pycparser/_ast_gen.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f7d330ba694ec21e278264b90bfbe483ca2b7d3
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/_ast_gen.py
@@ -0,0 +1,336 @@
+#-----------------------------------------------------------------
+# _ast_gen.py
+#
+# Generates the AST Node classes from a specification given in
+# a configuration file
+#
+# The design of this module was inspired by astgen.py from the
+# Python 2.5 code-base.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+from string import Template
+
+
+class ASTCodeGenerator(object):
+    def __init__(self, cfg_filename='_c_ast.cfg'):
+        """ Initialize the code generator from a configuration
+            file.
+        """
+        self.cfg_filename = cfg_filename
+        self.node_cfg = [NodeCfg(name, contents)
+            for (name, contents) in self.parse_cfgfile(cfg_filename)]
+
+    def generate(self, file=None):
+        """ Generates the code into file, an open file buffer.
+        """
+        src = Template(_PROLOGUE_COMMENT).substitute(
+            cfg_filename=self.cfg_filename)
+
+        src += _PROLOGUE_CODE
+        for node_cfg in self.node_cfg:
+            src += node_cfg.generate_source() + '\n\n'
+
+        file.write(src)
+
+    def parse_cfgfile(self, filename):
+        """ Parse the configuration file and yield pairs of
+            (name, contents) for each node.
+        """
+        with open(filename, "r") as f:
+            for line in f:
+                line = line.strip()
+                if not line or line.startswith('#'):
+                    continue
+                colon_i = line.find(':')
+                lbracket_i = line.find('[')
+                rbracket_i = line.find(']')
+                if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i:
+                    raise RuntimeError("Invalid line in %s:\n%s\n" % (filename, line))
+
+                name = line[:colon_i]
+                val = line[lbracket_i + 1:rbracket_i]
+                vallist = [v.strip() for v in val.split(',')] if val else []
+                yield name, vallist
+
+
+class NodeCfg(object):
+    """ Node configuration.
+
+        name: node name
+        contents: a list of contents - attributes and child nodes
+        See comment at the top of the configuration file for details.
+    """
+
+    def __init__(self, name, contents):
+        self.name = name
+        self.all_entries = []
+        self.attr = []
+        self.child = []
+        self.seq_child = []
+
+        for entry in contents:
+            clean_entry = entry.rstrip('*')
+            self.all_entries.append(clean_entry)
+
+            if entry.endswith('**'):
+                self.seq_child.append(clean_entry)
+            elif entry.endswith('*'):
+                self.child.append(clean_entry)
+            else:
+                self.attr.append(entry)
+
+    def generate_source(self):
+        src = self._gen_init()
+        src += '\n' + self._gen_children()
+        src += '\n' + self._gen_iter()
+        src += '\n' + self._gen_attr_names()
+        return src
+
+    def _gen_init(self):
+        src = "class %s(Node):\n" % self.name
+
+        if self.all_entries:
+            args = ', '.join(self.all_entries)
+            slots = ', '.join("'{0}'".format(e) for e in self.all_entries)
+            slots += ", 'coord', '__weakref__'"
+            arglist = '(self, %s, coord=None)' % args
+        else:
+            slots = "'coord', '__weakref__'"
+            arglist = '(self, coord=None)'
+
+        src += "    __slots__ = (%s)\n" % slots
+        src += "    def __init__%s:\n" % arglist
+
+        for name in self.all_entries + ['coord']:
+            src += "        self.%s = %s\n" % (name, name)
+
+        return src
+
+    def _gen_children(self):
+        src = '    def children(self):\n'
+
+        if self.all_entries:
+            src += '        nodelist = []\n'
+
+            for child in self.child:
+                src += (
+                    '        if self.%(child)s is not None:' +
+                    ' nodelist.append(("%(child)s", self.%(child)s))\n') % (
+                        dict(child=child))
+
+            for seq_child in self.seq_child:
+                src += (
+                    '        for i, child in enumerate(self.%(child)s or []):\n'
+                    '            nodelist.append(("%(child)s[%%d]" %% i, child))\n') % (
+                        dict(child=seq_child))
+
+            src += '        return tuple(nodelist)\n'
+        else:
+            src += '        return ()\n'
+
+        return src
+
+    def _gen_iter(self):
+        src = '    def __iter__(self):\n'
+
+        if self.all_entries:
+            for child in self.child:
+                src += (
+                    '        if self.%(child)s is not None:\n' +
+                    '            yield self.%(child)s\n') % (dict(child=child))
+
+            for seq_child in self.seq_child:
+                src += (
+                    '        for child in (self.%(child)s or []):\n'
+                    '            yield child\n') % (dict(child=seq_child))
+
+            if not (self.child or self.seq_child):
+                # Empty generator
+                src += (
+                    '        return\n' +
+                    '        yield\n')
+        else:
+            # Empty generator
+            src += (
+                '        return\n' +
+                '        yield\n')
+
+        return src
+
+    def _gen_attr_names(self):
+        src = "    attr_names = (" + ''.join("%r, " % nm for nm in self.attr) + ')'
+        return src
+
+
+_PROLOGUE_COMMENT = \
+r'''#-----------------------------------------------------------------
+# ** ATTENTION **
+# This code was automatically generated from the file:
+# $cfg_filename
+#
+# Do not modify it directly. Modify the configuration file and
+# run the generator again.
+# ** ** *** ** **
+#
+# pycparser: c_ast.py
+#
+# AST Node classes.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+'''
+
+_PROLOGUE_CODE = r'''
+import sys
+
+def _repr(obj):
+    """
+    Get the representation of an object, with dedicated pprint-like format for lists.
+    """
+    if isinstance(obj, list):
+        return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]'
+    else:
+        return repr(obj)
+
+class Node(object):
+    __slots__ = ()
+    """ Abstract base class for AST nodes.
+    """
+    def __repr__(self):
+        """ Generates a python representation of the current node
+        """
+        result = self.__class__.__name__ + '('
+
+        indent = ''
+        separator = ''
+        for name in self.__slots__[:-2]:
+            result += separator
+            result += indent
+            result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n  ' + (' ' * (len(name) + len(self.__class__.__name__)))))
+
+            separator = ','
+            indent = '\n ' + (' ' * len(self.__class__.__name__))
+
+        result += indent + ')'
+
+        return result
+
+    def children(self):
+        """ A sequence of all children that are Nodes
+        """
+        pass
+
+    def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None):
+        """ Pretty print the Node and all its attributes and
+            children (recursively) to a buffer.
+
+            buf:
+                Open IO buffer into which the Node is printed.
+
+            offset:
+                Initial offset (amount of leading spaces)
+
+            attrnames:
+                True if you want to see the attribute names in
+                name=value pairs. False to only see the values.
+
+            nodenames:
+                True if you want to see the actual node names
+                within their parents.
+
+            showcoord:
+                Do you want the coordinates of each Node to be
+                displayed.
+        """
+        lead = ' ' * offset
+        if nodenames and _my_node_name is not None:
+            buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ')
+        else:
+            buf.write(lead + self.__class__.__name__+ ': ')
+
+        if self.attr_names:
+            if attrnames:
+                nvlist = [(n, getattr(self,n)) for n in self.attr_names]
+                attrstr = ', '.join('%s=%s' % nv for nv in nvlist)
+            else:
+                vlist = [getattr(self, n) for n in self.attr_names]
+                attrstr = ', '.join('%s' % v for v in vlist)
+            buf.write(attrstr)
+
+        if showcoord:
+            buf.write(' (at %s)' % self.coord)
+        buf.write('\n')
+
+        for (child_name, child) in self.children():
+            child.show(
+                buf,
+                offset=offset + 2,
+                attrnames=attrnames,
+                nodenames=nodenames,
+                showcoord=showcoord,
+                _my_node_name=child_name)
+
+
+class NodeVisitor(object):
+    """ A base NodeVisitor class for visiting c_ast nodes.
+        Subclass it and define your own visit_XXX methods, where
+        XXX is the class name you want to visit with these
+        methods.
+
+        For example:
+
+        class ConstantVisitor(NodeVisitor):
+            def __init__(self):
+                self.values = []
+
+            def visit_Constant(self, node):
+                self.values.append(node.value)
+
+        Creates a list of values of all the constant nodes
+        encountered below the given node. To use it:
+
+        cv = ConstantVisitor()
+        cv.visit(node)
+
+        Notes:
+
+        *   generic_visit() will be called for AST nodes for which
+            no visit_XXX method was defined.
+        *   The children of nodes for which a visit_XXX was
+            defined will not be visited - if you need this, call
+            generic_visit() on the node.
+            You can use:
+                NodeVisitor.generic_visit(self, node)
+        *   Modeled after Python's own AST visiting facilities
+            (the ast module of Python 3.0)
+    """
+
+    _method_cache = None
+
+    def visit(self, node):
+        """ Visit a node.
+        """
+
+        if self._method_cache is None:
+            self._method_cache = {}
+
+        visitor = self._method_cache.get(node.__class__.__name__, None)
+        if visitor is None:
+            method = 'visit_' + node.__class__.__name__
+            visitor = getattr(self, method, self.generic_visit)
+            self._method_cache[node.__class__.__name__] = visitor
+
+        return visitor(node)
+
+    def generic_visit(self, node):
+        """ Called if no explicit visitor function exists for a
+            node. Implements preorder visiting of the node.
+        """
+        for c in node:
+            self.visit(c)
+
+'''
diff --git a/.venv/lib/python3.8/site-packages/pycparser/_build_tables.py b/.venv/lib/python3.8/site-packages/pycparser/_build_tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f3710795ca3f32e29d70d47e2a6447c00994fe4
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/_build_tables.py
@@ -0,0 +1,40 @@
+#-----------------------------------------------------------------
+# pycparser: _build_tables.py
+#
+# A dummy for generating the lexing/parsing tables and and
+# compiling them into .pyc for faster execution in optimized mode.
+# Also generates AST code from the configuration file.
+# Should be called from the pycparser directory.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+# Insert '.' and '..' as first entries to the search path for modules.
+# Restricted environments like embeddable python do not include the
+# current working directory on startup.
+import importlib
+import sys
+sys.path[0:0] = ['.', '..']
+
+# Generate c_ast.py
+from _ast_gen import ASTCodeGenerator
+ast_gen = ASTCodeGenerator('_c_ast.cfg')
+ast_gen.generate(open('c_ast.py', 'w'))
+
+from pycparser import c_parser
+
+# Generates the tables
+#
+c_parser.CParser(
+    lex_optimize=True,
+    yacc_debug=False,
+    yacc_optimize=True)
+
+# Load to compile into .pyc
+#
+importlib.invalidate_caches()
+
+import lextab
+import yacctab
+import c_ast
diff --git a/.venv/lib/python3.8/site-packages/pycparser/_c_ast.cfg b/.venv/lib/python3.8/site-packages/pycparser/_c_ast.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0626533e8adf517da897ec047c8deb6ad41c38c9
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/_c_ast.cfg
@@ -0,0 +1,195 @@
+#-----------------------------------------------------------------
+# pycparser: _c_ast.cfg
+#
+# Defines the AST Node classes used in pycparser.
+#
+# Each entry is a Node sub-class name, listing the attributes
+# and child nodes of the class:
+#   <name>*     - a child node
+#   <name>**    - a sequence of child nodes
+#   <name>      - an attribute
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+# ArrayDecl is a nested declaration of an array with the given type.
+# dim: the dimension (for example, constant 42)
+# dim_quals: list of dimension qualifiers, to support C99's allowing 'const'
+#            and 'static' within the array dimension in function declarations.
+ArrayDecl: [type*, dim*, dim_quals]
+
+ArrayRef: [name*, subscript*]
+
+# op: =, +=, /= etc.
+#
+Assignment: [op, lvalue*, rvalue*]
+
+Alignas: [alignment*]
+
+BinaryOp: [op, left*, right*]
+
+Break: []
+
+Case: [expr*, stmts**]
+
+Cast: [to_type*, expr*]
+
+# Compound statement in C99 is a list of block items (declarations or
+# statements).
+#
+Compound: [block_items**]
+
+# Compound literal (anonymous aggregate) for C99.
+# (type-name) {initializer_list}
+# type: the typename
+# init: InitList for the initializer list
+#
+CompoundLiteral: [type*, init*]
+
+# type: int, char, float, string, etc.
+#
+Constant: [type, value]
+
+Continue: []
+
+# name: the variable being declared
+# quals: list of qualifiers (const, volatile)
+# funcspec: list function specifiers (i.e. inline in C99)
+# storage: list of storage specifiers (extern, register, etc.)
+# type: declaration type (probably nested with all the modifiers)
+# init: initialization value, or None
+# bitsize: bit field size, or None
+#
+Decl: [name, quals, align, storage, funcspec, type*, init*, bitsize*]
+
+DeclList: [decls**]
+
+Default: [stmts**]
+
+DoWhile: [cond*, stmt*]
+
+# Represents the ellipsis (...) parameter in a function
+# declaration
+#
+EllipsisParam: []
+
+# An empty statement (a semicolon ';' on its own)
+#
+EmptyStatement: []
+
+# Enumeration type specifier
+# name: an optional ID
+# values: an EnumeratorList
+#
+Enum: [name, values*]
+
+# A name/value pair for enumeration values
+#
+Enumerator: [name, value*]
+
+# A list of enumerators
+#
+EnumeratorList: [enumerators**]
+
+# A list of expressions separated by the comma operator.
+#
+ExprList: [exprs**]
+
+# This is the top of the AST, representing a single C file (a
+# translation unit in K&R jargon). It contains a list of
+# "external-declaration"s, which is either declarations (Decl),
+# Typedef or function definitions (FuncDef).
+#
+FileAST: [ext**]
+
+# for (init; cond; next) stmt
+#
+For: [init*, cond*, next*, stmt*]
+
+# name: Id
+# args: ExprList
+#
+FuncCall: [name*, args*]
+
+# type <decl>(args)
+#
+FuncDecl: [args*, type*]
+
+# Function definition: a declarator for the function name and
+# a body, which is a compound statement.
+# There's an optional list of parameter declarations for old
+# K&R-style definitions
+#
+FuncDef: [decl*, param_decls**, body*]
+
+Goto: [name]
+
+ID: [name]
+
+# Holder for types that are a simple identifier (e.g. the built
+# ins void, char etc. and typedef-defined types)
+#
+IdentifierType: [names]
+
+If: [cond*, iftrue*, iffalse*]
+
+# An initialization list used for compound literals.
+#
+InitList: [exprs**]
+
+Label: [name, stmt*]
+
+# A named initializer for C99.
+# The name of a NamedInitializer is a sequence of Nodes, because
+# names can be hierarchical and contain constant expressions.
+#
+NamedInitializer: [name**, expr*]
+
+# a list of comma separated function parameter declarations
+#
+ParamList: [params**]
+
+PtrDecl: [quals, type*]
+
+Return: [expr*]
+
+StaticAssert: [cond*, message*]
+
+# name: struct tag name
+# decls: declaration of members
+#
+Struct: [name, decls**]
+
+# type: . or ->
+# name.field or name->field
+#
+StructRef: [name*, type, field*]
+
+Switch: [cond*, stmt*]
+
+# cond ? iftrue : iffalse
+#
+TernaryOp: [cond*, iftrue*, iffalse*]
+
+# A base type declaration
+#
+TypeDecl: [declname, quals, align, type*]
+
+# A typedef declaration.
+# Very similar to Decl, but without some attributes
+#
+Typedef: [name, quals, storage, type*]
+
+Typename: [name, quals, align, type*]
+
+UnaryOp: [op, expr*]
+
+# name: union tag name
+# decls: declaration of members
+#
+Union: [name, decls**]
+
+While: [cond*, stmt*]
+
+Pragma: [string]
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ast_transforms.py b/.venv/lib/python3.8/site-packages/pycparser/ast_transforms.py
new file mode 100644
index 0000000000000000000000000000000000000000..367dcf54c57db47429fae8a67325237e7c048e17
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ast_transforms.py
@@ -0,0 +1,164 @@
+#------------------------------------------------------------------------------
+# pycparser: ast_transforms.py
+#
+# Some utilities used by the parser to create a friendlier AST.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#------------------------------------------------------------------------------
+
+from . import c_ast
+
+
+def fix_switch_cases(switch_node):
+    """ The 'case' statements in a 'switch' come out of parsing with one
+        child node, so subsequent statements are just tucked to the parent
+        Compound. Additionally, consecutive (fall-through) case statements
+        come out messy. This is a peculiarity of the C grammar. The following:
+
+            switch (myvar) {
+                case 10:
+                    k = 10;
+                    p = k + 1;
+                    return 10;
+                case 20:
+                case 30:
+                    return 20;
+                default:
+                    break;
+            }
+
+        Creates this tree (pseudo-dump):
+
+            Switch
+                ID: myvar
+                Compound:
+                    Case 10:
+                        k = 10
+                    p = k + 1
+                    return 10
+                    Case 20:
+                        Case 30:
+                            return 20
+                    Default:
+                        break
+
+        The goal of this transform is to fix this mess, turning it into the
+        following:
+
+            Switch
+                ID: myvar
+                Compound:
+                    Case 10:
+                        k = 10
+                        p = k + 1
+                        return 10
+                    Case 20:
+                    Case 30:
+                        return 20
+                    Default:
+                        break
+
+        A fixed AST node is returned. The argument may be modified.
+    """
+    assert isinstance(switch_node, c_ast.Switch)
+    if not isinstance(switch_node.stmt, c_ast.Compound):
+        return switch_node
+
+    # The new Compound child for the Switch, which will collect children in the
+    # correct order
+    new_compound = c_ast.Compound([], switch_node.stmt.coord)
+
+    # The last Case/Default node
+    last_case = None
+
+    # Goes over the children of the Compound below the Switch, adding them
+    # either directly below new_compound or below the last Case as appropriate
+    # (for `switch(cond) {}`, block_items would have been None)
+    for child in (switch_node.stmt.block_items or []):
+        if isinstance(child, (c_ast.Case, c_ast.Default)):
+            # If it's a Case/Default:
+            # 1. Add it to the Compound and mark as "last case"
+            # 2. If its immediate child is also a Case or Default, promote it
+            #    to a sibling.
+            new_compound.block_items.append(child)
+            _extract_nested_case(child, new_compound.block_items)
+            last_case = new_compound.block_items[-1]
+        else:
+            # Other statements are added as children to the last case, if it
+            # exists.
+            if last_case is None:
+                new_compound.block_items.append(child)
+            else:
+                last_case.stmts.append(child)
+
+    switch_node.stmt = new_compound
+    return switch_node
+
+
+def _extract_nested_case(case_node, stmts_list):
+    """ Recursively extract consecutive Case statements that are made nested
+        by the parser and add them to the stmts_list.
+    """
+    if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):
+        stmts_list.append(case_node.stmts.pop())
+        _extract_nested_case(stmts_list[-1], stmts_list)
+
+
+def fix_atomic_specifiers(decl):
+    """ Atomic specifiers like _Atomic(type) are unusually structured,
+        conferring a qualifier upon the contained type.
+
+        This function fixes a decl with atomic specifiers to have a sane AST
+        structure, by removing spurious Typename->TypeDecl pairs and attaching
+        the _Atomic qualifier in the right place.
+    """
+    # There can be multiple levels of _Atomic in a decl; fix them until a
+    # fixed point is reached.
+    while True:
+        decl, found = _fix_atomic_specifiers_once(decl)
+        if not found:
+            break
+
+    # Make sure to add an _Atomic qual on the topmost decl if needed. Also
+    # restore the declname on the innermost TypeDecl (it gets placed in the
+    # wrong place during construction).
+    typ = decl
+    while not isinstance(typ, c_ast.TypeDecl):
+        try:
+            typ = typ.type
+        except AttributeError:
+            return decl
+    if '_Atomic' in typ.quals and '_Atomic' not in decl.quals:
+        decl.quals.append('_Atomic')
+    if typ.declname is None:
+        typ.declname = decl.name
+
+    return decl
+
+
+def _fix_atomic_specifiers_once(decl):
+    """ Performs one 'fix' round of atomic specifiers.
+        Returns (modified_decl, found) where found is True iff a fix was made.
+    """
+    parent = decl
+    grandparent = None
+    node = decl.type
+    while node is not None:
+        if isinstance(node, c_ast.Typename) and '_Atomic' in node.quals:
+            break
+        try:
+            grandparent = parent
+            parent = node
+            node = node.type
+        except AttributeError:
+            # If we've reached a node without a `type` field, it means we won't
+            # find what we're looking for at this point; give up the search
+            # and return the original decl unmodified.
+            return decl, False
+
+    assert isinstance(parent, c_ast.TypeDecl)
+    grandparent.type = node.type
+    if '_Atomic' not in node.type.quals:
+        node.type.quals.append('_Atomic')
+    return decl, True
diff --git a/.venv/lib/python3.8/site-packages/pycparser/c_ast.py b/.venv/lib/python3.8/site-packages/pycparser/c_ast.py
new file mode 100644
index 0000000000000000000000000000000000000000..6575a2ad395a3fd43c44e296b010e6b33c7eefd0
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/c_ast.py
@@ -0,0 +1,1125 @@
+#-----------------------------------------------------------------
+# ** ATTENTION **
+# This code was automatically generated from the file:
+# _c_ast.cfg
+#
+# Do not modify it directly. Modify the configuration file and
+# run the generator again.
+# ** ** *** ** **
+#
+# pycparser: c_ast.py
+#
+# AST Node classes.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+
+import sys
+
+def _repr(obj):
+    """
+    Get the representation of an object, with dedicated pprint-like format for lists.
+    """
+    if isinstance(obj, list):
+        return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]'
+    else:
+        return repr(obj)
+
+class Node(object):
+    __slots__ = ()
+    """ Abstract base class for AST nodes.
+    """
+    def __repr__(self):
+        """ Generates a python representation of the current node
+        """
+        result = self.__class__.__name__ + '('
+
+        indent = ''
+        separator = ''
+        for name in self.__slots__[:-2]:
+            result += separator
+            result += indent
+            result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n  ' + (' ' * (len(name) + len(self.__class__.__name__)))))
+
+            separator = ','
+            indent = '\n ' + (' ' * len(self.__class__.__name__))
+
+        result += indent + ')'
+
+        return result
+
+    def children(self):
+        """ A sequence of all children that are Nodes
+        """
+        pass
+
+    def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None):
+        """ Pretty print the Node and all its attributes and
+            children (recursively) to a buffer.
+
+            buf:
+                Open IO buffer into which the Node is printed.
+
+            offset:
+                Initial offset (amount of leading spaces)
+
+            attrnames:
+                True if you want to see the attribute names in
+                name=value pairs. False to only see the values.
+
+            nodenames:
+                True if you want to see the actual node names
+                within their parents.
+
+            showcoord:
+                Do you want the coordinates of each Node to be
+                displayed.
+        """
+        lead = ' ' * offset
+        if nodenames and _my_node_name is not None:
+            buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ')
+        else:
+            buf.write(lead + self.__class__.__name__+ ': ')
+
+        if self.attr_names:
+            if attrnames:
+                nvlist = [(n, getattr(self,n)) for n in self.attr_names]
+                attrstr = ', '.join('%s=%s' % nv for nv in nvlist)
+            else:
+                vlist = [getattr(self, n) for n in self.attr_names]
+                attrstr = ', '.join('%s' % v for v in vlist)
+            buf.write(attrstr)
+
+        if showcoord:
+            buf.write(' (at %s)' % self.coord)
+        buf.write('\n')
+
+        for (child_name, child) in self.children():
+            child.show(
+                buf,
+                offset=offset + 2,
+                attrnames=attrnames,
+                nodenames=nodenames,
+                showcoord=showcoord,
+                _my_node_name=child_name)
+
+
+class NodeVisitor(object):
+    """ A base NodeVisitor class for visiting c_ast nodes.
+        Subclass it and define your own visit_XXX methods, where
+        XXX is the class name you want to visit with these
+        methods.
+
+        For example:
+
+        class ConstantVisitor(NodeVisitor):
+            def __init__(self):
+                self.values = []
+
+            def visit_Constant(self, node):
+                self.values.append(node.value)
+
+        Creates a list of values of all the constant nodes
+        encountered below the given node. To use it:
+
+        cv = ConstantVisitor()
+        cv.visit(node)
+
+        Notes:
+
+        *   generic_visit() will be called for AST nodes for which
+            no visit_XXX method was defined.
+        *   The children of nodes for which a visit_XXX was
+            defined will not be visited - if you need this, call
+            generic_visit() on the node.
+            You can use:
+                NodeVisitor.generic_visit(self, node)
+        *   Modeled after Python's own AST visiting facilities
+            (the ast module of Python 3.0)
+    """
+
+    _method_cache = None
+
+    def visit(self, node):
+        """ Visit a node.
+        """
+
+        if self._method_cache is None:
+            self._method_cache = {}
+
+        visitor = self._method_cache.get(node.__class__.__name__, None)
+        if visitor is None:
+            method = 'visit_' + node.__class__.__name__
+            visitor = getattr(self, method, self.generic_visit)
+            self._method_cache[node.__class__.__name__] = visitor
+
+        return visitor(node)
+
+    def generic_visit(self, node):
+        """ Called if no explicit visitor function exists for a
+            node. Implements preorder visiting of the node.
+        """
+        for c in node:
+            self.visit(c)
+
+class ArrayDecl(Node):
+    __slots__ = ('type', 'dim', 'dim_quals', 'coord', '__weakref__')
+    def __init__(self, type, dim, dim_quals, coord=None):
+        self.type = type
+        self.dim = dim
+        self.dim_quals = dim_quals
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        if self.dim is not None: nodelist.append(("dim", self.dim))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+        if self.dim is not None:
+            yield self.dim
+
+    attr_names = ('dim_quals', )
+
+class ArrayRef(Node):
+    __slots__ = ('name', 'subscript', 'coord', '__weakref__')
+    def __init__(self, name, subscript, coord=None):
+        self.name = name
+        self.subscript = subscript
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.name is not None: nodelist.append(("name", self.name))
+        if self.subscript is not None: nodelist.append(("subscript", self.subscript))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.name is not None:
+            yield self.name
+        if self.subscript is not None:
+            yield self.subscript
+
+    attr_names = ()
+
+class Assignment(Node):
+    __slots__ = ('op', 'lvalue', 'rvalue', 'coord', '__weakref__')
+    def __init__(self, op, lvalue, rvalue, coord=None):
+        self.op = op
+        self.lvalue = lvalue
+        self.rvalue = rvalue
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.lvalue is not None: nodelist.append(("lvalue", self.lvalue))
+        if self.rvalue is not None: nodelist.append(("rvalue", self.rvalue))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.lvalue is not None:
+            yield self.lvalue
+        if self.rvalue is not None:
+            yield self.rvalue
+
+    attr_names = ('op', )
+
+class Alignas(Node):
+    __slots__ = ('alignment', 'coord', '__weakref__')
+    def __init__(self, alignment, coord=None):
+        self.alignment = alignment
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.alignment is not None: nodelist.append(("alignment", self.alignment))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.alignment is not None:
+            yield self.alignment
+
+    attr_names = ()
+
+class BinaryOp(Node):
+    __slots__ = ('op', 'left', 'right', 'coord', '__weakref__')
+    def __init__(self, op, left, right, coord=None):
+        self.op = op
+        self.left = left
+        self.right = right
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.left is not None: nodelist.append(("left", self.left))
+        if self.right is not None: nodelist.append(("right", self.right))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.left is not None:
+            yield self.left
+        if self.right is not None:
+            yield self.right
+
+    attr_names = ('op', )
+
+class Break(Node):
+    __slots__ = ('coord', '__weakref__')
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+class Case(Node):
+    __slots__ = ('expr', 'stmts', 'coord', '__weakref__')
+    def __init__(self, expr, stmts, coord=None):
+        self.expr = expr
+        self.stmts = stmts
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None: nodelist.append(("expr", self.expr))
+        for i, child in enumerate(self.stmts or []):
+            nodelist.append(("stmts[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+        for child in (self.stmts or []):
+            yield child
+
+    attr_names = ()
+
+class Cast(Node):
+    __slots__ = ('to_type', 'expr', 'coord', '__weakref__')
+    def __init__(self, to_type, expr, coord=None):
+        self.to_type = to_type
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.to_type is not None: nodelist.append(("to_type", self.to_type))
+        if self.expr is not None: nodelist.append(("expr", self.expr))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.to_type is not None:
+            yield self.to_type
+        if self.expr is not None:
+            yield self.expr
+
+    attr_names = ()
+
+class Compound(Node):
+    __slots__ = ('block_items', 'coord', '__weakref__')
+    def __init__(self, block_items, coord=None):
+        self.block_items = block_items
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.block_items or []):
+            nodelist.append(("block_items[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.block_items or []):
+            yield child
+
+    attr_names = ()
+
+class CompoundLiteral(Node):
+    __slots__ = ('type', 'init', 'coord', '__weakref__')
+    def __init__(self, type, init, coord=None):
+        self.type = type
+        self.init = init
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        if self.init is not None: nodelist.append(("init", self.init))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+        if self.init is not None:
+            yield self.init
+
+    attr_names = ()
+
+class Constant(Node):
+    __slots__ = ('type', 'value', 'coord', '__weakref__')
+    def __init__(self, type, value, coord=None):
+        self.type = type
+        self.value = value
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ('type', 'value', )
+
+class Continue(Node):
+    __slots__ = ('coord', '__weakref__')
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+class Decl(Node):
+    __slots__ = ('name', 'quals', 'align', 'storage', 'funcspec', 'type', 'init', 'bitsize', 'coord', '__weakref__')
+    def __init__(self, name, quals, align, storage, funcspec, type, init, bitsize, coord=None):
+        self.name = name
+        self.quals = quals
+        self.align = align
+        self.storage = storage
+        self.funcspec = funcspec
+        self.type = type
+        self.init = init
+        self.bitsize = bitsize
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        if self.init is not None: nodelist.append(("init", self.init))
+        if self.bitsize is not None: nodelist.append(("bitsize", self.bitsize))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+        if self.init is not None:
+            yield self.init
+        if self.bitsize is not None:
+            yield self.bitsize
+
+    attr_names = ('name', 'quals', 'align', 'storage', 'funcspec', )
+
+class DeclList(Node):
+    __slots__ = ('decls', 'coord', '__weakref__')
+    def __init__(self, decls, coord=None):
+        self.decls = decls
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.decls or []):
+            nodelist.append(("decls[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.decls or []):
+            yield child
+
+    attr_names = ()
+
+class Default(Node):
+    __slots__ = ('stmts', 'coord', '__weakref__')
+    def __init__(self, stmts, coord=None):
+        self.stmts = stmts
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.stmts or []):
+            nodelist.append(("stmts[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.stmts or []):
+            yield child
+
+    attr_names = ()
+
+class DoWhile(Node):
+    __slots__ = ('cond', 'stmt', 'coord', '__weakref__')
+    def __init__(self, cond, stmt, coord=None):
+        self.cond = cond
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.stmt is not None: nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+class EllipsisParam(Node):
+    __slots__ = ('coord', '__weakref__')
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+class EmptyStatement(Node):
+    __slots__ = ('coord', '__weakref__')
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+class Enum(Node):
+    __slots__ = ('name', 'values', 'coord', '__weakref__')
+    def __init__(self, name, values, coord=None):
+        self.name = name
+        self.values = values
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.values is not None: nodelist.append(("values", self.values))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.values is not None:
+            yield self.values
+
+    attr_names = ('name', )
+
+class Enumerator(Node):
+    __slots__ = ('name', 'value', 'coord', '__weakref__')
+    def __init__(self, name, value, coord=None):
+        self.name = name
+        self.value = value
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.value is not None: nodelist.append(("value", self.value))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.value is not None:
+            yield self.value
+
+    attr_names = ('name', )
+
+class EnumeratorList(Node):
+    __slots__ = ('enumerators', 'coord', '__weakref__')
+    def __init__(self, enumerators, coord=None):
+        self.enumerators = enumerators
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.enumerators or []):
+            nodelist.append(("enumerators[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.enumerators or []):
+            yield child
+
+    attr_names = ()
+
+class ExprList(Node):
+    __slots__ = ('exprs', 'coord', '__weakref__')
+    def __init__(self, exprs, coord=None):
+        self.exprs = exprs
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.exprs or []):
+            nodelist.append(("exprs[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.exprs or []):
+            yield child
+
+    attr_names = ()
+
+class FileAST(Node):
+    __slots__ = ('ext', 'coord', '__weakref__')
+    def __init__(self, ext, coord=None):
+        self.ext = ext
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.ext or []):
+            nodelist.append(("ext[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.ext or []):
+            yield child
+
+    attr_names = ()
+
+class For(Node):
+    __slots__ = ('init', 'cond', 'next', 'stmt', 'coord', '__weakref__')
+    def __init__(self, init, cond, next, stmt, coord=None):
+        self.init = init
+        self.cond = cond
+        self.next = next
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.init is not None: nodelist.append(("init", self.init))
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.next is not None: nodelist.append(("next", self.next))
+        if self.stmt is not None: nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.init is not None:
+            yield self.init
+        if self.cond is not None:
+            yield self.cond
+        if self.next is not None:
+            yield self.next
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+class FuncCall(Node):
+    __slots__ = ('name', 'args', 'coord', '__weakref__')
+    def __init__(self, name, args, coord=None):
+        self.name = name
+        self.args = args
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.name is not None: nodelist.append(("name", self.name))
+        if self.args is not None: nodelist.append(("args", self.args))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.name is not None:
+            yield self.name
+        if self.args is not None:
+            yield self.args
+
+    attr_names = ()
+
+class FuncDecl(Node):
+    __slots__ = ('args', 'type', 'coord', '__weakref__')
+    def __init__(self, args, type, coord=None):
+        self.args = args
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.args is not None: nodelist.append(("args", self.args))
+        if self.type is not None: nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.args is not None:
+            yield self.args
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ()
+
+class FuncDef(Node):
+    __slots__ = ('decl', 'param_decls', 'body', 'coord', '__weakref__')
+    def __init__(self, decl, param_decls, body, coord=None):
+        self.decl = decl
+        self.param_decls = param_decls
+        self.body = body
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.decl is not None: nodelist.append(("decl", self.decl))
+        if self.body is not None: nodelist.append(("body", self.body))
+        for i, child in enumerate(self.param_decls or []):
+            nodelist.append(("param_decls[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.decl is not None:
+            yield self.decl
+        if self.body is not None:
+            yield self.body
+        for child in (self.param_decls or []):
+            yield child
+
+    attr_names = ()
+
+class Goto(Node):
+    __slots__ = ('name', 'coord', '__weakref__')
+    def __init__(self, name, coord=None):
+        self.name = name
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ('name', )
+
+class ID(Node):
+    __slots__ = ('name', 'coord', '__weakref__')
+    def __init__(self, name, coord=None):
+        self.name = name
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ('name', )
+
+class IdentifierType(Node):
+    __slots__ = ('names', 'coord', '__weakref__')
+    def __init__(self, names, coord=None):
+        self.names = names
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ('names', )
+
+class If(Node):
+    __slots__ = ('cond', 'iftrue', 'iffalse', 'coord', '__weakref__')
+    def __init__(self, cond, iftrue, iffalse, coord=None):
+        self.cond = cond
+        self.iftrue = iftrue
+        self.iffalse = iffalse
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.iftrue is not None: nodelist.append(("iftrue", self.iftrue))
+        if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.iftrue is not None:
+            yield self.iftrue
+        if self.iffalse is not None:
+            yield self.iffalse
+
+    attr_names = ()
+
+class InitList(Node):
+    __slots__ = ('exprs', 'coord', '__weakref__')
+    def __init__(self, exprs, coord=None):
+        self.exprs = exprs
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.exprs or []):
+            nodelist.append(("exprs[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.exprs or []):
+            yield child
+
+    attr_names = ()
+
+class Label(Node):
+    __slots__ = ('name', 'stmt', 'coord', '__weakref__')
+    def __init__(self, name, stmt, coord=None):
+        self.name = name
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.stmt is not None: nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ('name', )
+
+class NamedInitializer(Node):
+    __slots__ = ('name', 'expr', 'coord', '__weakref__')
+    def __init__(self, name, expr, coord=None):
+        self.name = name
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None: nodelist.append(("expr", self.expr))
+        for i, child in enumerate(self.name or []):
+            nodelist.append(("name[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+        for child in (self.name or []):
+            yield child
+
+    attr_names = ()
+
+class ParamList(Node):
+    __slots__ = ('params', 'coord', '__weakref__')
+    def __init__(self, params, coord=None):
+        self.params = params
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.params or []):
+            nodelist.append(("params[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.params or []):
+            yield child
+
+    attr_names = ()
+
+class PtrDecl(Node):
+    __slots__ = ('quals', 'type', 'coord', '__weakref__')
+    def __init__(self, quals, type, coord=None):
+        self.quals = quals
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ('quals', )
+
+class Return(Node):
+    __slots__ = ('expr', 'coord', '__weakref__')
+    def __init__(self, expr, coord=None):
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None: nodelist.append(("expr", self.expr))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+
+    attr_names = ()
+
+class StaticAssert(Node):
+    __slots__ = ('cond', 'message', 'coord', '__weakref__')
+    def __init__(self, cond, message, coord=None):
+        self.cond = cond
+        self.message = message
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.message is not None: nodelist.append(("message", self.message))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.message is not None:
+            yield self.message
+
+    attr_names = ()
+
+class Struct(Node):
+    __slots__ = ('name', 'decls', 'coord', '__weakref__')
+    def __init__(self, name, decls, coord=None):
+        self.name = name
+        self.decls = decls
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.decls or []):
+            nodelist.append(("decls[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.decls or []):
+            yield child
+
+    attr_names = ('name', )
+
+class StructRef(Node):
+    __slots__ = ('name', 'type', 'field', 'coord', '__weakref__')
+    def __init__(self, name, type, field, coord=None):
+        self.name = name
+        self.type = type
+        self.field = field
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.name is not None: nodelist.append(("name", self.name))
+        if self.field is not None: nodelist.append(("field", self.field))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.name is not None:
+            yield self.name
+        if self.field is not None:
+            yield self.field
+
+    attr_names = ('type', )
+
+class Switch(Node):
+    __slots__ = ('cond', 'stmt', 'coord', '__weakref__')
+    def __init__(self, cond, stmt, coord=None):
+        self.cond = cond
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.stmt is not None: nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+class TernaryOp(Node):
+    __slots__ = ('cond', 'iftrue', 'iffalse', 'coord', '__weakref__')
+    def __init__(self, cond, iftrue, iffalse, coord=None):
+        self.cond = cond
+        self.iftrue = iftrue
+        self.iffalse = iffalse
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.iftrue is not None: nodelist.append(("iftrue", self.iftrue))
+        if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.iftrue is not None:
+            yield self.iftrue
+        if self.iffalse is not None:
+            yield self.iffalse
+
+    attr_names = ()
+
+class TypeDecl(Node):
+    __slots__ = ('declname', 'quals', 'align', 'type', 'coord', '__weakref__')
+    def __init__(self, declname, quals, align, type, coord=None):
+        self.declname = declname
+        self.quals = quals
+        self.align = align
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ('declname', 'quals', 'align', )
+
+class Typedef(Node):
+    __slots__ = ('name', 'quals', 'storage', 'type', 'coord', '__weakref__')
+    def __init__(self, name, quals, storage, type, coord=None):
+        self.name = name
+        self.quals = quals
+        self.storage = storage
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ('name', 'quals', 'storage', )
+
+class Typename(Node):
+    __slots__ = ('name', 'quals', 'align', 'type', 'coord', '__weakref__')
+    def __init__(self, name, quals, align, type, coord=None):
+        self.name = name
+        self.quals = quals
+        self.align = align
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None: nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ('name', 'quals', 'align', )
+
+class UnaryOp(Node):
+    __slots__ = ('op', 'expr', 'coord', '__weakref__')
+    def __init__(self, op, expr, coord=None):
+        self.op = op
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None: nodelist.append(("expr", self.expr))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+
+    attr_names = ('op', )
+
+class Union(Node):
+    __slots__ = ('name', 'decls', 'coord', '__weakref__')
+    def __init__(self, name, decls, coord=None):
+        self.name = name
+        self.decls = decls
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.decls or []):
+            nodelist.append(("decls[%d]" % i, child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in (self.decls or []):
+            yield child
+
+    attr_names = ('name', )
+
+class While(Node):
+    __slots__ = ('cond', 'stmt', 'coord', '__weakref__')
+    def __init__(self, cond, stmt, coord=None):
+        self.cond = cond
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None: nodelist.append(("cond", self.cond))
+        if self.stmt is not None: nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+class Pragma(Node):
+    __slots__ = ('string', 'coord', '__weakref__')
+    def __init__(self, string, coord=None):
+        self.string = string
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ('string', )
+
diff --git a/.venv/lib/python3.8/site-packages/pycparser/c_generator.py b/.venv/lib/python3.8/site-packages/pycparser/c_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..1057b2c62e26feb74f2c1d0536de22140579fc8b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/c_generator.py
@@ -0,0 +1,502 @@
+#------------------------------------------------------------------------------
+# pycparser: c_generator.py
+#
+# C code generator from pycparser AST nodes.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#------------------------------------------------------------------------------
+from . import c_ast
+
+
+class CGenerator(object):
+    """ Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
+        return a value from each visit method, using string accumulation in
+        generic_visit.
+    """
+    def __init__(self, reduce_parentheses=False):
+        """ Constructs C-code generator
+
+            reduce_parentheses:
+                if True, eliminates needless parentheses on binary operators
+        """
+        # Statements start with indentation of self.indent_level spaces, using
+        # the _make_indent method.
+        self.indent_level = 0
+        self.reduce_parentheses = reduce_parentheses
+
+    def _make_indent(self):
+        return ' ' * self.indent_level
+
+    def visit(self, node):
+        method = 'visit_' + node.__class__.__name__
+        return getattr(self, method, self.generic_visit)(node)
+
+    def generic_visit(self, node):
+        if node is None:
+            return ''
+        else:
+            return ''.join(self.visit(c) for c_name, c in node.children())
+
+    def visit_Constant(self, n):
+        return n.value
+
+    def visit_ID(self, n):
+        return n.name
+
+    def visit_Pragma(self, n):
+        ret = '#pragma'
+        if n.string:
+            ret += ' ' + n.string
+        return ret
+
+    def visit_ArrayRef(self, n):
+        arrref = self._parenthesize_unless_simple(n.name)
+        return arrref + '[' + self.visit(n.subscript) + ']'
+
+    def visit_StructRef(self, n):
+        sref = self._parenthesize_unless_simple(n.name)
+        return sref + n.type + self.visit(n.field)
+
+    def visit_FuncCall(self, n):
+        fref = self._parenthesize_unless_simple(n.name)
+        return fref + '(' + self.visit(n.args) + ')'
+
+    def visit_UnaryOp(self, n):
+        if n.op == 'sizeof':
+            # Always parenthesize the argument of sizeof since it can be
+            # a name.
+            return 'sizeof(%s)' % self.visit(n.expr)
+        else:
+            operand = self._parenthesize_unless_simple(n.expr)
+            if n.op == 'p++':
+                return '%s++' % operand
+            elif n.op == 'p--':
+                return '%s--' % operand
+            else:
+                return '%s%s' % (n.op, operand)
+
+    # Precedence map of binary operators:
+    precedence_map = {
+        # Should be in sync with c_parser.CParser.precedence
+        # Higher numbers are stronger binding
+        '||': 0,  # weakest binding
+        '&&': 1,
+        '|': 2,
+        '^': 3,
+        '&': 4,
+        '==': 5, '!=': 5,
+        '>': 6, '>=': 6, '<': 6, '<=': 6,
+        '>>': 7, '<<': 7,
+        '+': 8, '-': 8,
+        '*': 9, '/': 9, '%': 9  # strongest binding
+    }
+
+    def visit_BinaryOp(self, n):
+        # Note: all binary operators are left-to-right associative
+        #
+        # If `n.left.op` has a stronger or equally binding precedence in
+        # comparison to `n.op`, no parenthesis are needed for the left:
+        # e.g., `(a*b) + c` is equivalent to `a*b + c`, as well as
+        #       `(a+b) - c` is equivalent to `a+b - c` (same precedence).
+        # If the left operator is weaker binding than the current, then
+        # parentheses are necessary:
+        # e.g., `(a+b) * c` is NOT equivalent to `a+b * c`.
+        lval_str = self._parenthesize_if(
+            n.left,
+            lambda d: not (self._is_simple_node(d) or
+                      self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and
+                      self.precedence_map[d.op] >= self.precedence_map[n.op]))
+        # If `n.right.op` has a stronger -but not equal- binding precedence,
+        # parenthesis can be omitted on the right:
+        # e.g., `a + (b*c)` is equivalent to `a + b*c`.
+        # If the right operator is weaker or equally binding, then parentheses
+        # are necessary:
+        # e.g., `a * (b+c)` is NOT equivalent to `a * b+c` and
+        #       `a - (b+c)` is NOT equivalent to `a - b+c` (same precedence).
+        rval_str = self._parenthesize_if(
+            n.right,
+            lambda d: not (self._is_simple_node(d) or
+                      self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and
+                      self.precedence_map[d.op] > self.precedence_map[n.op]))
+        return '%s %s %s' % (lval_str, n.op, rval_str)
+
+    def visit_Assignment(self, n):
+        rval_str = self._parenthesize_if(
+                            n.rvalue,
+                            lambda n: isinstance(n, c_ast.Assignment))
+        return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str)
+
+    def visit_IdentifierType(self, n):
+        return ' '.join(n.names)
+
+    def _visit_expr(self, n):
+        if isinstance(n, c_ast.InitList):
+            return '{' + self.visit(n) + '}'
+        elif isinstance(n, c_ast.ExprList):
+            return '(' + self.visit(n) + ')'
+        else:
+            return self.visit(n)
+
+    def visit_Decl(self, n, no_type=False):
+        # no_type is used when a Decl is part of a DeclList, where the type is
+        # explicitly only for the first declaration in a list.
+        #
+        s = n.name if no_type else self._generate_decl(n)
+        if n.bitsize: s += ' : ' + self.visit(n.bitsize)
+        if n.init:
+            s += ' = ' + self._visit_expr(n.init)
+        return s
+
+    def visit_DeclList(self, n):
+        s = self.visit(n.decls[0])
+        if len(n.decls) > 1:
+            s += ', ' + ', '.join(self.visit_Decl(decl, no_type=True)
+                                    for decl in n.decls[1:])
+        return s
+
+    def visit_Typedef(self, n):
+        s = ''
+        if n.storage: s += ' '.join(n.storage) + ' '
+        s += self._generate_type(n.type)
+        return s
+
+    def visit_Cast(self, n):
+        s = '(' + self._generate_type(n.to_type, emit_declname=False) + ')'
+        return s + ' ' + self._parenthesize_unless_simple(n.expr)
+
+    def visit_ExprList(self, n):
+        visited_subexprs = []
+        for expr in n.exprs:
+            visited_subexprs.append(self._visit_expr(expr))
+        return ', '.join(visited_subexprs)
+
+    def visit_InitList(self, n):
+        visited_subexprs = []
+        for expr in n.exprs:
+            visited_subexprs.append(self._visit_expr(expr))
+        return ', '.join(visited_subexprs)
+
+    def visit_Enum(self, n):
+        return self._generate_struct_union_enum(n, name='enum')
+
+    def visit_Alignas(self, n):
+        return '_Alignas({})'.format(self.visit(n.alignment))
+
+    def visit_Enumerator(self, n):
+        if not n.value:
+            return '{indent}{name},\n'.format(
+                indent=self._make_indent(),
+                name=n.name,
+            )
+        else:
+            return '{indent}{name} = {value},\n'.format(
+                indent=self._make_indent(),
+                name=n.name,
+                value=self.visit(n.value),
+            )
+
+    def visit_FuncDef(self, n):
+        decl = self.visit(n.decl)
+        self.indent_level = 0
+        body = self.visit(n.body)
+        if n.param_decls:
+            knrdecls = ';\n'.join(self.visit(p) for p in n.param_decls)
+            return decl + '\n' + knrdecls + ';\n' + body + '\n'
+        else:
+            return decl + '\n' + body + '\n'
+
+    def visit_FileAST(self, n):
+        s = ''
+        for ext in n.ext:
+            if isinstance(ext, c_ast.FuncDef):
+                s += self.visit(ext)
+            elif isinstance(ext, c_ast.Pragma):
+                s += self.visit(ext) + '\n'
+            else:
+                s += self.visit(ext) + ';\n'
+        return s
+
+    def visit_Compound(self, n):
+        s = self._make_indent() + '{\n'
+        self.indent_level += 2
+        if n.block_items:
+            s += ''.join(self._generate_stmt(stmt) for stmt in n.block_items)
+        self.indent_level -= 2
+        s += self._make_indent() + '}\n'
+        return s
+
+    def visit_CompoundLiteral(self, n):
+        return '(' + self.visit(n.type) + '){' + self.visit(n.init) + '}'
+
+
+    def visit_EmptyStatement(self, n):
+        return ';'
+
+    def visit_ParamList(self, n):
+        return ', '.join(self.visit(param) for param in n.params)
+
+    def visit_Return(self, n):
+        s = 'return'
+        if n.expr: s += ' ' + self.visit(n.expr)
+        return s + ';'
+
+    def visit_Break(self, n):
+        return 'break;'
+
+    def visit_Continue(self, n):
+        return 'continue;'
+
+    def visit_TernaryOp(self, n):
+        s  = '(' + self._visit_expr(n.cond) + ') ? '
+        s += '(' + self._visit_expr(n.iftrue) + ') : '
+        s += '(' + self._visit_expr(n.iffalse) + ')'
+        return s
+
+    def visit_If(self, n):
+        s = 'if ('
+        if n.cond: s += self.visit(n.cond)
+        s += ')\n'
+        s += self._generate_stmt(n.iftrue, add_indent=True)
+        if n.iffalse:
+            s += self._make_indent() + 'else\n'
+            s += self._generate_stmt(n.iffalse, add_indent=True)
+        return s
+
+    def visit_For(self, n):
+        s = 'for ('
+        if n.init: s += self.visit(n.init)
+        s += ';'
+        if n.cond: s += ' ' + self.visit(n.cond)
+        s += ';'
+        if n.next: s += ' ' + self.visit(n.next)
+        s += ')\n'
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        return s
+
+    def visit_While(self, n):
+        s = 'while ('
+        if n.cond: s += self.visit(n.cond)
+        s += ')\n'
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        return s
+
+    def visit_DoWhile(self, n):
+        s = 'do\n'
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        s += self._make_indent() + 'while ('
+        if n.cond: s += self.visit(n.cond)
+        s += ');'
+        return s
+
+    def visit_StaticAssert(self, n):
+        s = '_Static_assert('
+        s += self.visit(n.cond)
+        if n.message:
+            s += ','
+            s += self.visit(n.message)
+        s += ')'
+        return s
+
+    def visit_Switch(self, n):
+        s = 'switch (' + self.visit(n.cond) + ')\n'
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        return s
+
+    def visit_Case(self, n):
+        s = 'case ' + self.visit(n.expr) + ':\n'
+        for stmt in n.stmts:
+            s += self._generate_stmt(stmt, add_indent=True)
+        return s
+
+    def visit_Default(self, n):
+        s = 'default:\n'
+        for stmt in n.stmts:
+            s += self._generate_stmt(stmt, add_indent=True)
+        return s
+
+    def visit_Label(self, n):
+        return n.name + ':\n' + self._generate_stmt(n.stmt)
+
+    def visit_Goto(self, n):
+        return 'goto ' + n.name + ';'
+
+    def visit_EllipsisParam(self, n):
+        return '...'
+
+    def visit_Struct(self, n):
+        return self._generate_struct_union_enum(n, 'struct')
+
+    def visit_Typename(self, n):
+        return self._generate_type(n.type)
+
+    def visit_Union(self, n):
+        return self._generate_struct_union_enum(n, 'union')
+
+    def visit_NamedInitializer(self, n):
+        s = ''
+        for name in n.name:
+            if isinstance(name, c_ast.ID):
+                s += '.' + name.name
+            else:
+                s += '[' + self.visit(name) + ']'
+        s += ' = ' + self._visit_expr(n.expr)
+        return s
+
+    def visit_FuncDecl(self, n):
+        return self._generate_type(n)
+
+    def visit_ArrayDecl(self, n):
+        return self._generate_type(n, emit_declname=False)
+
+    def visit_TypeDecl(self, n):
+        return self._generate_type(n, emit_declname=False)
+
+    def visit_PtrDecl(self, n):
+        return self._generate_type(n, emit_declname=False)
+
+    def _generate_struct_union_enum(self, n, name):
+        """ Generates code for structs, unions, and enums. name should be
+            'struct', 'union', or 'enum'.
+        """
+        if name in ('struct', 'union'):
+            members = n.decls
+            body_function = self._generate_struct_union_body
+        else:
+            assert name == 'enum'
+            members = None if n.values is None else n.values.enumerators
+            body_function = self._generate_enum_body
+        s = name + ' ' + (n.name or '')
+        if members is not None:
+            # None means no members
+            # Empty sequence means an empty list of members
+            s += '\n'
+            s += self._make_indent()
+            self.indent_level += 2
+            s += '{\n'
+            s += body_function(members)
+            self.indent_level -= 2
+            s += self._make_indent() + '}'
+        return s
+
+    def _generate_struct_union_body(self, members):
+        return ''.join(self._generate_stmt(decl) for decl in members)
+
+    def _generate_enum_body(self, members):
+        # `[:-2] + '\n'` removes the final `,` from the enumerator list
+        return ''.join(self.visit(value) for value in members)[:-2] + '\n'
+
+    def _generate_stmt(self, n, add_indent=False):
+        """ Generation from a statement node. This method exists as a wrapper
+            for individual visit_* methods to handle different treatment of
+            some statements in this context.
+        """
+        typ = type(n)
+        if add_indent: self.indent_level += 2
+        indent = self._make_indent()
+        if add_indent: self.indent_level -= 2
+
+        if typ in (
+                c_ast.Decl, c_ast.Assignment, c_ast.Cast, c_ast.UnaryOp,
+                c_ast.BinaryOp, c_ast.TernaryOp, c_ast.FuncCall, c_ast.ArrayRef,
+                c_ast.StructRef, c_ast.Constant, c_ast.ID, c_ast.Typedef,
+                c_ast.ExprList):
+            # These can also appear in an expression context so no semicolon
+            # is added to them automatically
+            #
+            return indent + self.visit(n) + ';\n'
+        elif typ in (c_ast.Compound,):
+            # No extra indentation required before the opening brace of a
+            # compound - because it consists of multiple lines it has to
+            # compute its own indentation.
+            #
+            return self.visit(n)
+        elif typ in (c_ast.If,):
+            return indent + self.visit(n)
+        else:
+            return indent + self.visit(n) + '\n'
+
+    def _generate_decl(self, n):
+        """ Generation from a Decl node.
+        """
+        s = ''
+        if n.funcspec: s = ' '.join(n.funcspec) + ' '
+        if n.storage: s += ' '.join(n.storage) + ' '
+        if n.align: s += self.visit(n.align[0]) + ' '
+        s += self._generate_type(n.type)
+        return s
+
+    def _generate_type(self, n, modifiers=[], emit_declname = True):
+        """ Recursive generation from a type node. n is the type node.
+            modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers
+            encountered on the way down to a TypeDecl, to allow proper
+            generation from it.
+        """
+        typ = type(n)
+        #~ print(n, modifiers)
+
+        if typ == c_ast.TypeDecl:
+            s = ''
+            if n.quals: s += ' '.join(n.quals) + ' '
+            s += self.visit(n.type)
+
+            nstr = n.declname if n.declname and emit_declname else ''
+            # Resolve modifiers.
+            # Wrap in parens to distinguish pointer to array and pointer to
+            # function syntax.
+            #
+            for i, modifier in enumerate(modifiers):
+                if isinstance(modifier, c_ast.ArrayDecl):
+                    if (i != 0 and
+                        isinstance(modifiers[i - 1], c_ast.PtrDecl)):
+                            nstr = '(' + nstr + ')'
+                    nstr += '['
+                    if modifier.dim_quals:
+                        nstr += ' '.join(modifier.dim_quals) + ' '
+                    nstr += self.visit(modifier.dim) + ']'
+                elif isinstance(modifier, c_ast.FuncDecl):
+                    if (i != 0 and
+                        isinstance(modifiers[i - 1], c_ast.PtrDecl)):
+                            nstr = '(' + nstr + ')'
+                    nstr += '(' + self.visit(modifier.args) + ')'
+                elif isinstance(modifier, c_ast.PtrDecl):
+                    if modifier.quals:
+                        nstr = '* %s%s' % (' '.join(modifier.quals),
+                                           ' ' + nstr if nstr else '')
+                    else:
+                        nstr = '*' + nstr
+            if nstr: s += ' ' + nstr
+            return s
+        elif typ == c_ast.Decl:
+            return self._generate_decl(n.type)
+        elif typ == c_ast.Typename:
+            return self._generate_type(n.type, emit_declname = emit_declname)
+        elif typ == c_ast.IdentifierType:
+            return ' '.join(n.names) + ' '
+        elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl):
+            return self._generate_type(n.type, modifiers + [n],
+                                       emit_declname = emit_declname)
+        else:
+            return self.visit(n)
+
+    def _parenthesize_if(self, n, condition):
+        """ Visits 'n' and returns its string representation, parenthesized
+            if the condition function applied to the node returns True.
+        """
+        s = self._visit_expr(n)
+        if condition(n):
+            return '(' + s + ')'
+        else:
+            return s
+
+    def _parenthesize_unless_simple(self, n):
+        """ Common use case for _parenthesize_if
+        """
+        return self._parenthesize_if(n, lambda d: not self._is_simple_node(d))
+
+    def _is_simple_node(self, n):
+        """ Returns True for nodes that are "simple" - i.e. nodes that always
+            have higher precedence than operators.
+        """
+        return isinstance(n, (c_ast.Constant, c_ast.ID, c_ast.ArrayRef,
+                              c_ast.StructRef, c_ast.FuncCall))
diff --git a/.venv/lib/python3.8/site-packages/pycparser/c_lexer.py b/.venv/lib/python3.8/site-packages/pycparser/c_lexer.py
new file mode 100644
index 0000000000000000000000000000000000000000..22c64bc761ceb9916db4f33cc0f85357fd9a945a
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/c_lexer.py
@@ -0,0 +1,555 @@
+#------------------------------------------------------------------------------
+# pycparser: c_lexer.py
+#
+# CLexer class: lexer for the C language
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#------------------------------------------------------------------------------
+import re
+
+from .ply import lex
+from .ply.lex import TOKEN
+
+
+class CLexer(object):
+    """ A lexer for the C language. After building it, set the
+        input text with input(), and call token() to get new
+        tokens.
+
+        The public attribute filename can be set to an initial
+        filename, but the lexer will update it upon #line
+        directives.
+    """
+    def __init__(self, error_func, on_lbrace_func, on_rbrace_func,
+                 type_lookup_func):
+        """ Create a new Lexer.
+
+            error_func:
+                An error function. Will be called with an error
+                message, line and column as arguments, in case of
+                an error during lexing.
+
+            on_lbrace_func, on_rbrace_func:
+                Called when an LBRACE or RBRACE is encountered
+                (likely to push/pop type_lookup_func's scope)
+
+            type_lookup_func:
+                A type lookup function. Given a string, it must
+                return True IFF this string is a name of a type
+                that was defined with a typedef earlier.
+        """
+        self.error_func = error_func
+        self.on_lbrace_func = on_lbrace_func
+        self.on_rbrace_func = on_rbrace_func
+        self.type_lookup_func = type_lookup_func
+        self.filename = ''
+
+        # Keeps track of the last token returned from self.token()
+        self.last_token = None
+
+        # Allow either "# line" or "# <num>" to support GCC's
+        # cpp output
+        #
+        self.line_pattern = re.compile(r'([ \t]*line\W)|([ \t]*\d+)')
+        self.pragma_pattern = re.compile(r'[ \t]*pragma\W')
+
+    def build(self, **kwargs):
+        """ Builds the lexer from the specification. Must be
+            called after the lexer object is created.
+
+            This method exists separately, because the PLY
+            manual warns against calling lex.lex inside
+            __init__
+        """
+        self.lexer = lex.lex(object=self, **kwargs)
+
+    def reset_lineno(self):
+        """ Resets the internal line number counter of the lexer.
+        """
+        self.lexer.lineno = 1
+
+    def input(self, text):
+        self.lexer.input(text)
+
+    def token(self):
+        self.last_token = self.lexer.token()
+        return self.last_token
+
+    def find_tok_column(self, token):
+        """ Find the column of the token in its line.
+        """
+        last_cr = self.lexer.lexdata.rfind('\n', 0, token.lexpos)
+        return token.lexpos - last_cr
+
+    ######################--   PRIVATE   --######################
+
+    ##
+    ## Internal auxiliary methods
+    ##
+    def _error(self, msg, token):
+        location = self._make_tok_location(token)
+        self.error_func(msg, location[0], location[1])
+        self.lexer.skip(1)
+
+    def _make_tok_location(self, token):
+        return (token.lineno, self.find_tok_column(token))
+
+    ##
+    ## Reserved keywords
+    ##
+    keywords = (
+        'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST',
+        'CONTINUE', 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN',
+        'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG',
+        'REGISTER', 'OFFSETOF',
+        'RESTRICT', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT',
+        'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID',
+        'VOLATILE', 'WHILE', '__INT128',
+    )
+
+    keywords_new = (
+        '_BOOL', '_COMPLEX',
+        '_NORETURN', '_THREAD_LOCAL', '_STATIC_ASSERT',
+        '_ATOMIC', '_ALIGNOF', '_ALIGNAS',
+        '_PRAGMA',
+        )
+
+    keyword_map = {}
+
+    for keyword in keywords:
+        keyword_map[keyword.lower()] = keyword
+
+    for keyword in keywords_new:
+        keyword_map[keyword[:2].upper() + keyword[2:].lower()] = keyword
+
+    ##
+    ## All the tokens recognized by the lexer
+    ##
+    tokens = keywords + keywords_new + (
+        # Identifiers
+        'ID',
+
+        # Type identifiers (identifiers previously defined as
+        # types with typedef)
+        'TYPEID',
+
+        # constants
+        'INT_CONST_DEC', 'INT_CONST_OCT', 'INT_CONST_HEX', 'INT_CONST_BIN', 'INT_CONST_CHAR',
+        'FLOAT_CONST', 'HEX_FLOAT_CONST',
+        'CHAR_CONST',
+        'WCHAR_CONST',
+        'U8CHAR_CONST',
+        'U16CHAR_CONST',
+        'U32CHAR_CONST',
+
+        # String literals
+        'STRING_LITERAL',
+        'WSTRING_LITERAL',
+        'U8STRING_LITERAL',
+        'U16STRING_LITERAL',
+        'U32STRING_LITERAL',
+
+        # Operators
+        'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD',
+        'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
+        'LOR', 'LAND', 'LNOT',
+        'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
+
+        # Assignment
+        'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL',
+        'PLUSEQUAL', 'MINUSEQUAL',
+        'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL',
+        'OREQUAL',
+
+        # Increment/decrement
+        'PLUSPLUS', 'MINUSMINUS',
+
+        # Structure dereference (->)
+        'ARROW',
+
+        # Conditional operator (?)
+        'CONDOP',
+
+        # Delimiters
+        'LPAREN', 'RPAREN',         # ( )
+        'LBRACKET', 'RBRACKET',     # [ ]
+        'LBRACE', 'RBRACE',         # { }
+        'COMMA', 'PERIOD',          # . ,
+        'SEMI', 'COLON',            # ; :
+
+        # Ellipsis (...)
+        'ELLIPSIS',
+
+        # pre-processor
+        'PPHASH',       # '#'
+        'PPPRAGMA',     # 'pragma'
+        'PPPRAGMASTR',
+    )
+
+    ##
+    ## Regexes for use in tokens
+    ##
+    ##
+
+    # valid C identifiers (K&R2: A.2.3), plus '$' (supported by some compilers)
+    identifier = r'[a-zA-Z_$][0-9a-zA-Z_$]*'
+
+    hex_prefix = '0[xX]'
+    hex_digits = '[0-9a-fA-F]+'
+    bin_prefix = '0[bB]'
+    bin_digits = '[01]+'
+
+    # integer constants (K&R2: A.2.5.1)
+    integer_suffix_opt = r'(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?'
+    decimal_constant = '(0'+integer_suffix_opt+')|([1-9][0-9]*'+integer_suffix_opt+')'
+    octal_constant = '0[0-7]*'+integer_suffix_opt
+    hex_constant = hex_prefix+hex_digits+integer_suffix_opt
+    bin_constant = bin_prefix+bin_digits+integer_suffix_opt
+
+    bad_octal_constant = '0[0-7]*[89]'
+
+    # character constants (K&R2: A.2.5.2)
+    # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
+    # directives with Windows paths as filenames (..\..\dir\file)
+    # For the same reason, decimal_escape allows all digit sequences. We want to
+    # parse all correct code, even if it means to sometimes parse incorrect
+    # code.
+    #
+    # The original regexes were taken verbatim from the C syntax definition,
+    # and were later modified to avoid worst-case exponential running time.
+    #
+    #   simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
+    #   decimal_escape = r"""(\d+)"""
+    #   hex_escape = r"""(x[0-9a-fA-F]+)"""
+    #   bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
+    #
+    # The following modifications were made to avoid the ambiguity that allowed backtracking:
+    # (https://github.com/eliben/pycparser/issues/61)
+    #
+    # - \x was removed from simple_escape, unless it was not followed by a hex digit, to avoid ambiguity with hex_escape.
+    # - hex_escape allows one or more hex characters, but requires that the next character(if any) is not hex
+    # - decimal_escape allows one or more decimal characters, but requires that the next character(if any) is not a decimal
+    # - bad_escape does not allow any decimals (8-9), to avoid conflicting with the permissive decimal_escape.
+    #
+    # Without this change, python's `re` module would recursively try parsing each ambiguous escape sequence in multiple ways.
+    # e.g. `\123` could be parsed as `\1`+`23`, `\12`+`3`, and `\123`.
+
+    simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))"""
+    decimal_escape = r"""(\d+)(?!\d)"""
+    hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])"""
+    bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])"""
+
+    escape_sequence = r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))'
+
+    # This complicated regex with lookahead might be slow for strings, so because all of the valid escapes (including \x) allowed
+    # 0 or more non-escaped characters after the first character, simple_escape+decimal_escape+hex_escape got simplified to
+
+    escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])"""
+
+    cconst_char = r"""([^'\\\n]|"""+escape_sequence+')'
+    char_const = "'"+cconst_char+"'"
+    wchar_const = 'L'+char_const
+    u8char_const = 'u8'+char_const
+    u16char_const = 'u'+char_const
+    u32char_const = 'U'+char_const
+    multicharacter_constant = "'"+cconst_char+"{2,4}'"
+    unmatched_quote = "('"+cconst_char+"*\\n)|('"+cconst_char+"*$)"
+    bad_char_const = r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+bad_escape+r"""[^'\n]*')"""
+
+    # string literals (K&R2: A.2.6)
+    string_char = r"""([^"\\\n]|"""+escape_sequence_start_in_string+')'
+    string_literal = '"'+string_char+'*"'
+    wstring_literal = 'L'+string_literal
+    u8string_literal = 'u8'+string_literal
+    u16string_literal = 'u'+string_literal
+    u32string_literal = 'U'+string_literal
+    bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"'
+
+    # floating constants (K&R2: A.2.5.3)
+    exponent_part = r"""([eE][-+]?[0-9]+)"""
+    fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)"""
+    floating_constant = '(((('+fractional_constant+')'+exponent_part+'?)|([0-9]+'+exponent_part+'))[FfLl]?)'
+    binary_exponent_part = r'''([pP][+-]?[0-9]+)'''
+    hex_fractional_constant = '((('+hex_digits+r""")?\."""+hex_digits+')|('+hex_digits+r"""\.))"""
+    hex_floating_constant = '('+hex_prefix+'('+hex_digits+'|'+hex_fractional_constant+')'+binary_exponent_part+'[FfLl]?)'
+
+    ##
+    ## Lexer states: used for preprocessor \n-terminated directives
+    ##
+    states = (
+        # ppline: preprocessor line directives
+        #
+        ('ppline', 'exclusive'),
+
+        # pppragma: pragma
+        #
+        ('pppragma', 'exclusive'),
+    )
+
+    def t_PPHASH(self, t):
+        r'[ \t]*\#'
+        if self.line_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos):
+            t.lexer.begin('ppline')
+            self.pp_line = self.pp_filename = None
+        elif self.pragma_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos):
+            t.lexer.begin('pppragma')
+        else:
+            t.type = 'PPHASH'
+            return t
+
+    ##
+    ## Rules for the ppline state
+    ##
+    @TOKEN(string_literal)
+    def t_ppline_FILENAME(self, t):
+        if self.pp_line is None:
+            self._error('filename before line number in #line', t)
+        else:
+            self.pp_filename = t.value.lstrip('"').rstrip('"')
+
+    @TOKEN(decimal_constant)
+    def t_ppline_LINE_NUMBER(self, t):
+        if self.pp_line is None:
+            self.pp_line = t.value
+        else:
+            # Ignore: GCC's cpp sometimes inserts a numeric flag
+            # after the file name
+            pass
+
+    def t_ppline_NEWLINE(self, t):
+        r'\n'
+        if self.pp_line is None:
+            self._error('line number missing in #line', t)
+        else:
+            self.lexer.lineno = int(self.pp_line)
+
+            if self.pp_filename is not None:
+                self.filename = self.pp_filename
+
+        t.lexer.begin('INITIAL')
+
+    def t_ppline_PPLINE(self, t):
+        r'line'
+        pass
+
+    t_ppline_ignore = ' \t'
+
+    def t_ppline_error(self, t):
+        self._error('invalid #line directive', t)
+
+    ##
+    ## Rules for the pppragma state
+    ##
+    def t_pppragma_NEWLINE(self, t):
+        r'\n'
+        t.lexer.lineno += 1
+        t.lexer.begin('INITIAL')
+
+    def t_pppragma_PPPRAGMA(self, t):
+        r'pragma'
+        return t
+
+    t_pppragma_ignore = ' \t'
+
+    def t_pppragma_STR(self, t):
+        '.+'
+        t.type = 'PPPRAGMASTR'
+        return t
+
+    def t_pppragma_error(self, t):
+        self._error('invalid #pragma directive', t)
+
+    ##
+    ## Rules for the normal state
+    ##
+    t_ignore = ' \t'
+
+    # Newlines
+    def t_NEWLINE(self, t):
+        r'\n+'
+        t.lexer.lineno += t.value.count("\n")
+
+    # Operators
+    t_PLUS              = r'\+'
+    t_MINUS             = r'-'
+    t_TIMES             = r'\*'
+    t_DIVIDE            = r'/'
+    t_MOD               = r'%'
+    t_OR                = r'\|'
+    t_AND               = r'&'
+    t_NOT               = r'~'
+    t_XOR               = r'\^'
+    t_LSHIFT            = r'<<'
+    t_RSHIFT            = r'>>'
+    t_LOR               = r'\|\|'
+    t_LAND              = r'&&'
+    t_LNOT              = r'!'
+    t_LT                = r'<'
+    t_GT                = r'>'
+    t_LE                = r'<='
+    t_GE                = r'>='
+    t_EQ                = r'=='
+    t_NE                = r'!='
+
+    # Assignment operators
+    t_EQUALS            = r'='
+    t_TIMESEQUAL        = r'\*='
+    t_DIVEQUAL          = r'/='
+    t_MODEQUAL          = r'%='
+    t_PLUSEQUAL         = r'\+='
+    t_MINUSEQUAL        = r'-='
+    t_LSHIFTEQUAL       = r'<<='
+    t_RSHIFTEQUAL       = r'>>='
+    t_ANDEQUAL          = r'&='
+    t_OREQUAL           = r'\|='
+    t_XOREQUAL          = r'\^='
+
+    # Increment/decrement
+    t_PLUSPLUS          = r'\+\+'
+    t_MINUSMINUS        = r'--'
+
+    # ->
+    t_ARROW             = r'->'
+
+    # ?
+    t_CONDOP            = r'\?'
+
+    # Delimiters
+    t_LPAREN            = r'\('
+    t_RPAREN            = r'\)'
+    t_LBRACKET          = r'\['
+    t_RBRACKET          = r'\]'
+    t_COMMA             = r','
+    t_PERIOD            = r'\.'
+    t_SEMI              = r';'
+    t_COLON             = r':'
+    t_ELLIPSIS          = r'\.\.\.'
+
+    # Scope delimiters
+    # To see why on_lbrace_func is needed, consider:
+    #   typedef char TT;
+    #   void foo(int TT) { TT = 10; }
+    #   TT x = 5;
+    # Outside the function, TT is a typedef, but inside (starting and ending
+    # with the braces) it's a parameter.  The trouble begins with yacc's
+    # lookahead token.  If we open a new scope in brace_open, then TT has
+    # already been read and incorrectly interpreted as TYPEID.  So, we need
+    # to open and close scopes from within the lexer.
+    # Similar for the TT immediately outside the end of the function.
+    #
+    @TOKEN(r'\{')
+    def t_LBRACE(self, t):
+        self.on_lbrace_func()
+        return t
+    @TOKEN(r'\}')
+    def t_RBRACE(self, t):
+        self.on_rbrace_func()
+        return t
+
+    t_STRING_LITERAL = string_literal
+
+    # The following floating and integer constants are defined as
+    # functions to impose a strict order (otherwise, decimal
+    # is placed before the others because its regex is longer,
+    # and this is bad)
+    #
+    @TOKEN(floating_constant)
+    def t_FLOAT_CONST(self, t):
+        return t
+
+    @TOKEN(hex_floating_constant)
+    def t_HEX_FLOAT_CONST(self, t):
+        return t
+
+    @TOKEN(hex_constant)
+    def t_INT_CONST_HEX(self, t):
+        return t
+
+    @TOKEN(bin_constant)
+    def t_INT_CONST_BIN(self, t):
+        return t
+
+    @TOKEN(bad_octal_constant)
+    def t_BAD_CONST_OCT(self, t):
+        msg = "Invalid octal constant"
+        self._error(msg, t)
+
+    @TOKEN(octal_constant)
+    def t_INT_CONST_OCT(self, t):
+        return t
+
+    @TOKEN(decimal_constant)
+    def t_INT_CONST_DEC(self, t):
+        return t
+
+    # Must come before bad_char_const, to prevent it from
+    # catching valid char constants as invalid
+    #
+    @TOKEN(multicharacter_constant)
+    def t_INT_CONST_CHAR(self, t):
+        return t
+
+    @TOKEN(char_const)
+    def t_CHAR_CONST(self, t):
+        return t
+
+    @TOKEN(wchar_const)
+    def t_WCHAR_CONST(self, t):
+        return t
+
+    @TOKEN(u8char_const)
+    def t_U8CHAR_CONST(self, t):
+        return t
+
+    @TOKEN(u16char_const)
+    def t_U16CHAR_CONST(self, t):
+        return t
+
+    @TOKEN(u32char_const)
+    def t_U32CHAR_CONST(self, t):
+        return t
+
+    @TOKEN(unmatched_quote)
+    def t_UNMATCHED_QUOTE(self, t):
+        msg = "Unmatched '"
+        self._error(msg, t)
+
+    @TOKEN(bad_char_const)
+    def t_BAD_CHAR_CONST(self, t):
+        msg = "Invalid char constant %s" % t.value
+        self._error(msg, t)
+
+    @TOKEN(wstring_literal)
+    def t_WSTRING_LITERAL(self, t):
+        return t
+
+    @TOKEN(u8string_literal)
+    def t_U8STRING_LITERAL(self, t):
+        return t
+
+    @TOKEN(u16string_literal)
+    def t_U16STRING_LITERAL(self, t):
+        return t
+
+    @TOKEN(u32string_literal)
+    def t_U32STRING_LITERAL(self, t):
+        return t
+
+    # unmatched string literals are caught by the preprocessor
+
+    @TOKEN(bad_string_literal)
+    def t_BAD_STRING_LITERAL(self, t):
+        msg = "String contains invalid escape code"
+        self._error(msg, t)
+
+    @TOKEN(identifier)
+    def t_ID(self, t):
+        t.type = self.keyword_map.get(t.value, "ID")
+        if t.type == 'ID' and self.type_lookup_func(t.value):
+            t.type = "TYPEID"
+        return t
+
+    def t_error(self, t):
+        msg = 'Illegal character %s' % repr(t.value[0])
+        self._error(msg, t)
diff --git a/.venv/lib/python3.8/site-packages/pycparser/c_parser.py b/.venv/lib/python3.8/site-packages/pycparser/c_parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..d31574a5a1d31c144cdbf9901dfdfe22cc2c6086
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/c_parser.py
@@ -0,0 +1,1950 @@
+#------------------------------------------------------------------------------
+# pycparser: c_parser.py
+#
+# CParser class: Parser and AST builder for the C language
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#------------------------------------------------------------------------------
+from .ply import yacc
+
+from . import c_ast
+from .c_lexer import CLexer
+from .plyparser import PLYParser, ParseError, parameterized, template
+from .ast_transforms import fix_switch_cases, fix_atomic_specifiers
+
+
+@template
+class CParser(PLYParser):
+    def __init__(
+            self,
+            lex_optimize=True,
+            lexer=CLexer,
+            lextab='pycparser.lextab',
+            yacc_optimize=True,
+            yacctab='pycparser.yacctab',
+            yacc_debug=False,
+            taboutputdir=''):
+        """ Create a new CParser.
+
+            Some arguments for controlling the debug/optimization
+            level of the parser are provided. The defaults are
+            tuned for release/performance mode.
+            The simple rules for using them are:
+            *) When tweaking CParser/CLexer, set these to False
+            *) When releasing a stable parser, set to True
+
+            lex_optimize:
+                Set to False when you're modifying the lexer.
+                Otherwise, changes in the lexer won't be used, if
+                some lextab.py file exists.
+                When releasing with a stable lexer, set to True
+                to save the re-generation of the lexer table on
+                each run.
+
+            lexer:
+                Set this parameter to define the lexer to use if
+                you're not using the default CLexer.
+
+            lextab:
+                Points to the lex table that's used for optimized
+                mode. Only if you're modifying the lexer and want
+                some tests to avoid re-generating the table, make
+                this point to a local lex table file (that's been
+                earlier generated with lex_optimize=True)
+
+            yacc_optimize:
+                Set to False when you're modifying the parser.
+                Otherwise, changes in the parser won't be used, if
+                some parsetab.py file exists.
+                When releasing with a stable parser, set to True
+                to save the re-generation of the parser table on
+                each run.
+
+            yacctab:
+                Points to the yacc table that's used for optimized
+                mode. Only if you're modifying the parser, make
+                this point to a local yacc table file
+
+            yacc_debug:
+                Generate a parser.out file that explains how yacc
+                built the parsing table from the grammar.
+
+            taboutputdir:
+                Set this parameter to control the location of generated
+                lextab and yacctab files.
+        """
+        self.clex = lexer(
+            error_func=self._lex_error_func,
+            on_lbrace_func=self._lex_on_lbrace_func,
+            on_rbrace_func=self._lex_on_rbrace_func,
+            type_lookup_func=self._lex_type_lookup_func)
+
+        self.clex.build(
+            optimize=lex_optimize,
+            lextab=lextab,
+            outputdir=taboutputdir)
+        self.tokens = self.clex.tokens
+
+        rules_with_opt = [
+            'abstract_declarator',
+            'assignment_expression',
+            'declaration_list',
+            'declaration_specifiers_no_type',
+            'designation',
+            'expression',
+            'identifier_list',
+            'init_declarator_list',
+            'id_init_declarator_list',
+            'initializer_list',
+            'parameter_type_list',
+            'block_item_list',
+            'type_qualifier_list',
+            'struct_declarator_list'
+        ]
+
+        for rule in rules_with_opt:
+            self._create_opt_rule(rule)
+
+        self.cparser = yacc.yacc(
+            module=self,
+            start='translation_unit_or_empty',
+            debug=yacc_debug,
+            optimize=yacc_optimize,
+            tabmodule=yacctab,
+            outputdir=taboutputdir)
+
+        # Stack of scopes for keeping track of symbols. _scope_stack[-1] is
+        # the current (topmost) scope. Each scope is a dictionary that
+        # specifies whether a name is a type. If _scope_stack[n][name] is
+        # True, 'name' is currently a type in the scope. If it's False,
+        # 'name' is used in the scope but not as a type (for instance, if we
+        # saw: int name;
+        # If 'name' is not a key in _scope_stack[n] then 'name' was not defined
+        # in this scope at all.
+        self._scope_stack = [dict()]
+
+        # Keeps track of the last token given to yacc (the lookahead token)
+        self._last_yielded_token = None
+
+    def parse(self, text, filename='', debug=False):
+        """ Parses C code and returns an AST.
+
+            text:
+                A string containing the C source code
+
+            filename:
+                Name of the file being parsed (for meaningful
+                error messages)
+
+            debug:
+                Debug flag to YACC
+        """
+        self.clex.filename = filename
+        self.clex.reset_lineno()
+        self._scope_stack = [dict()]
+        self._last_yielded_token = None
+        return self.cparser.parse(
+                input=text,
+                lexer=self.clex,
+                debug=debug)
+
+    ######################--   PRIVATE   --######################
+
+    def _push_scope(self):
+        self._scope_stack.append(dict())
+
+    def _pop_scope(self):
+        assert len(self._scope_stack) > 1
+        self._scope_stack.pop()
+
+    def _add_typedef_name(self, name, coord):
+        """ Add a new typedef name (ie a TYPEID) to the current scope
+        """
+        if not self._scope_stack[-1].get(name, True):
+            self._parse_error(
+                "Typedef %r previously declared as non-typedef "
+                "in this scope" % name, coord)
+        self._scope_stack[-1][name] = True
+
+    def _add_identifier(self, name, coord):
+        """ Add a new object, function, or enum member name (ie an ID) to the
+            current scope
+        """
+        if self._scope_stack[-1].get(name, False):
+            self._parse_error(
+                "Non-typedef %r previously declared as typedef "
+                "in this scope" % name, coord)
+        self._scope_stack[-1][name] = False
+
+    def _is_type_in_scope(self, name):
+        """ Is *name* a typedef-name in the current scope?
+        """
+        for scope in reversed(self._scope_stack):
+            # If name is an identifier in this scope it shadows typedefs in
+            # higher scopes.
+            in_scope = scope.get(name)
+            if in_scope is not None: return in_scope
+        return False
+
+    def _lex_error_func(self, msg, line, column):
+        self._parse_error(msg, self._coord(line, column))
+
+    def _lex_on_lbrace_func(self):
+        self._push_scope()
+
+    def _lex_on_rbrace_func(self):
+        self._pop_scope()
+
+    def _lex_type_lookup_func(self, name):
+        """ Looks up types that were previously defined with
+            typedef.
+            Passed to the lexer for recognizing identifiers that
+            are types.
+        """
+        is_type = self._is_type_in_scope(name)
+        return is_type
+
+    def _get_yacc_lookahead_token(self):
+        """ We need access to yacc's lookahead token in certain cases.
+            This is the last token yacc requested from the lexer, so we
+            ask the lexer.
+        """
+        return self.clex.last_token
+
+    # To understand what's going on here, read sections A.8.5 and
+    # A.8.6 of K&R2 very carefully.
+    #
+    # A C type consists of a basic type declaration, with a list
+    # of modifiers. For example:
+    #
+    # int *c[5];
+    #
+    # The basic declaration here is 'int c', and the pointer and
+    # the array are the modifiers.
+    #
+    # Basic declarations are represented by TypeDecl (from module c_ast) and the
+    # modifiers are FuncDecl, PtrDecl and ArrayDecl.
+    #
+    # The standard states that whenever a new modifier is parsed, it should be
+    # added to the end of the list of modifiers. For example:
+    #
+    # K&R2 A.8.6.2: Array Declarators
+    #
+    # In a declaration T D where D has the form
+    #   D1 [constant-expression-opt]
+    # and the type of the identifier in the declaration T D1 is
+    # "type-modifier T", the type of the
+    # identifier of D is "type-modifier array of T"
+    #
+    # This is what this method does. The declarator it receives
+    # can be a list of declarators ending with TypeDecl. It
+    # tacks the modifier to the end of this list, just before
+    # the TypeDecl.
+    #
+    # Additionally, the modifier may be a list itself. This is
+    # useful for pointers, that can come as a chain from the rule
+    # p_pointer. In this case, the whole modifier list is spliced
+    # into the new location.
+    def _type_modify_decl(self, decl, modifier):
+        """ Tacks a type modifier on a declarator, and returns
+            the modified declarator.
+
+            Note: the declarator and modifier may be modified
+        """
+        #~ print '****'
+        #~ decl.show(offset=3)
+        #~ modifier.show(offset=3)
+        #~ print '****'
+
+        modifier_head = modifier
+        modifier_tail = modifier
+
+        # The modifier may be a nested list. Reach its tail.
+        while modifier_tail.type:
+            modifier_tail = modifier_tail.type
+
+        # If the decl is a basic type, just tack the modifier onto it.
+        if isinstance(decl, c_ast.TypeDecl):
+            modifier_tail.type = decl
+            return modifier
+        else:
+            # Otherwise, the decl is a list of modifiers. Reach
+            # its tail and splice the modifier onto the tail,
+            # pointing to the underlying basic type.
+            decl_tail = decl
+
+            while not isinstance(decl_tail.type, c_ast.TypeDecl):
+                decl_tail = decl_tail.type
+
+            modifier_tail.type = decl_tail.type
+            decl_tail.type = modifier_head
+            return decl
+
+    # Due to the order in which declarators are constructed,
+    # they have to be fixed in order to look like a normal AST.
+    #
+    # When a declaration arrives from syntax construction, it has
+    # these problems:
+    # * The innermost TypeDecl has no type (because the basic
+    #   type is only known at the uppermost declaration level)
+    # * The declaration has no variable name, since that is saved
+    #   in the innermost TypeDecl
+    # * The typename of the declaration is a list of type
+    #   specifiers, and not a node. Here, basic identifier types
+    #   should be separated from more complex types like enums
+    #   and structs.
+    #
+    # This method fixes these problems.
+    def _fix_decl_name_type(self, decl, typename):
+        """ Fixes a declaration. Modifies decl.
+        """
+        # Reach the underlying basic type
+        #
+        type = decl
+        while not isinstance(type, c_ast.TypeDecl):
+            type = type.type
+
+        decl.name = type.declname
+        type.quals = decl.quals[:]
+
+        # The typename is a list of types. If any type in this
+        # list isn't an IdentifierType, it must be the only
+        # type in the list (it's illegal to declare "int enum ..")
+        # If all the types are basic, they're collected in the
+        # IdentifierType holder.
+        for tn in typename:
+            if not isinstance(tn, c_ast.IdentifierType):
+                if len(typename) > 1:
+                    self._parse_error(
+                        "Invalid multiple types specified", tn.coord)
+                else:
+                    type.type = tn
+                    return decl
+
+        if not typename:
+            # Functions default to returning int
+            #
+            if not isinstance(decl.type, c_ast.FuncDecl):
+                self._parse_error(
+                        "Missing type in declaration", decl.coord)
+            type.type = c_ast.IdentifierType(
+                    ['int'],
+                    coord=decl.coord)
+        else:
+            # At this point, we know that typename is a list of IdentifierType
+            # nodes. Concatenate all the names into a single list.
+            #
+            type.type = c_ast.IdentifierType(
+                [name for id in typename for name in id.names],
+                coord=typename[0].coord)
+        return decl
+
+    def _add_declaration_specifier(self, declspec, newspec, kind, append=False):
+        """ Declaration specifiers are represented by a dictionary
+            with the entries:
+            * qual: a list of type qualifiers
+            * storage: a list of storage type qualifiers
+            * type: a list of type specifiers
+            * function: a list of function specifiers
+            * alignment: a list of alignment specifiers
+
+            This method is given a declaration specifier, and a
+            new specifier of a given kind.
+            If `append` is True, the new specifier is added to the end of
+            the specifiers list, otherwise it's added at the beginning.
+            Returns the declaration specifier, with the new
+            specifier incorporated.
+        """
+        spec = declspec or dict(qual=[], storage=[], type=[], function=[], alignment=[])
+
+        if append:
+            spec[kind].append(newspec)
+        else:
+            spec[kind].insert(0, newspec)
+
+        return spec
+
+    def _build_declarations(self, spec, decls, typedef_namespace=False):
+        """ Builds a list of declarations all sharing the given specifiers.
+            If typedef_namespace is true, each declared name is added
+            to the "typedef namespace", which also includes objects,
+            functions, and enum constants.
+        """
+        is_typedef = 'typedef' in spec['storage']
+        declarations = []
+
+        # Bit-fields are allowed to be unnamed.
+        if decls[0].get('bitsize') is not None:
+            pass
+
+        # When redeclaring typedef names as identifiers in inner scopes, a
+        # problem can occur where the identifier gets grouped into
+        # spec['type'], leaving decl as None.  This can only occur for the
+        # first declarator.
+        elif decls[0]['decl'] is None:
+            if len(spec['type']) < 2 or len(spec['type'][-1].names) != 1 or \
+                    not self._is_type_in_scope(spec['type'][-1].names[0]):
+                coord = '?'
+                for t in spec['type']:
+                    if hasattr(t, 'coord'):
+                        coord = t.coord
+                        break
+                self._parse_error('Invalid declaration', coord)
+
+            # Make this look as if it came from "direct_declarator:ID"
+            decls[0]['decl'] = c_ast.TypeDecl(
+                declname=spec['type'][-1].names[0],
+                type=None,
+                quals=None,
+                align=spec['alignment'],
+                coord=spec['type'][-1].coord)
+            # Remove the "new" type's name from the end of spec['type']
+            del spec['type'][-1]
+
+        # A similar problem can occur where the declaration ends up looking
+        # like an abstract declarator.  Give it a name if this is the case.
+        elif not isinstance(decls[0]['decl'], (
+                c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType)):
+            decls_0_tail = decls[0]['decl']
+            while not isinstance(decls_0_tail, c_ast.TypeDecl):
+                decls_0_tail = decls_0_tail.type
+            if decls_0_tail.declname is None:
+                decls_0_tail.declname = spec['type'][-1].names[0]
+                del spec['type'][-1]
+
+        for decl in decls:
+            assert decl['decl'] is not None
+            if is_typedef:
+                declaration = c_ast.Typedef(
+                    name=None,
+                    quals=spec['qual'],
+                    storage=spec['storage'],
+                    type=decl['decl'],
+                    coord=decl['decl'].coord)
+            else:
+                declaration = c_ast.Decl(
+                    name=None,
+                    quals=spec['qual'],
+                    align=spec['alignment'],
+                    storage=spec['storage'],
+                    funcspec=spec['function'],
+                    type=decl['decl'],
+                    init=decl.get('init'),
+                    bitsize=decl.get('bitsize'),
+                    coord=decl['decl'].coord)
+
+            if isinstance(declaration.type, (
+                    c_ast.Enum, c_ast.Struct, c_ast.Union,
+                    c_ast.IdentifierType)):
+                fixed_decl = declaration
+            else:
+                fixed_decl = self._fix_decl_name_type(declaration, spec['type'])
+
+            # Add the type name defined by typedef to a
+            # symbol table (for usage in the lexer)
+            if typedef_namespace:
+                if is_typedef:
+                    self._add_typedef_name(fixed_decl.name, fixed_decl.coord)
+                else:
+                    self._add_identifier(fixed_decl.name, fixed_decl.coord)
+
+            fixed_decl = fix_atomic_specifiers(fixed_decl)
+            declarations.append(fixed_decl)
+
+        return declarations
+
+    def _build_function_definition(self, spec, decl, param_decls, body):
+        """ Builds a function definition.
+        """
+        if 'typedef' in spec['storage']:
+            self._parse_error("Invalid typedef", decl.coord)
+
+        declaration = self._build_declarations(
+            spec=spec,
+            decls=[dict(decl=decl, init=None)],
+            typedef_namespace=True)[0]
+
+        return c_ast.FuncDef(
+            decl=declaration,
+            param_decls=param_decls,
+            body=body,
+            coord=decl.coord)
+
+    def _select_struct_union_class(self, token):
+        """ Given a token (either STRUCT or UNION), selects the
+            appropriate AST class.
+        """
+        if token == 'struct':
+            return c_ast.Struct
+        else:
+            return c_ast.Union
+
+    ##
+    ## Precedence and associativity of operators
+    ##
+    # If this changes, c_generator.CGenerator.precedence_map needs to change as
+    # well
+    precedence = (
+        ('left', 'LOR'),
+        ('left', 'LAND'),
+        ('left', 'OR'),
+        ('left', 'XOR'),
+        ('left', 'AND'),
+        ('left', 'EQ', 'NE'),
+        ('left', 'GT', 'GE', 'LT', 'LE'),
+        ('left', 'RSHIFT', 'LSHIFT'),
+        ('left', 'PLUS', 'MINUS'),
+        ('left', 'TIMES', 'DIVIDE', 'MOD')
+    )
+
+    ##
+    ## Grammar productions
+    ## Implementation of the BNF defined in K&R2 A.13
+    ##
+
+    # Wrapper around a translation unit, to allow for empty input.
+    # Not strictly part of the C99 Grammar, but useful in practice.
+    def p_translation_unit_or_empty(self, p):
+        """ translation_unit_or_empty   : translation_unit
+                                        | empty
+        """
+        if p[1] is None:
+            p[0] = c_ast.FileAST([])
+        else:
+            p[0] = c_ast.FileAST(p[1])
+
+    def p_translation_unit_1(self, p):
+        """ translation_unit    : external_declaration
+        """
+        # Note: external_declaration is already a list
+        p[0] = p[1]
+
+    def p_translation_unit_2(self, p):
+        """ translation_unit    : translation_unit external_declaration
+        """
+        p[1].extend(p[2])
+        p[0] = p[1]
+
+    # Declarations always come as lists (because they can be
+    # several in one line), so we wrap the function definition
+    # into a list as well, to make the return value of
+    # external_declaration homogeneous.
+    def p_external_declaration_1(self, p):
+        """ external_declaration    : function_definition
+        """
+        p[0] = [p[1]]
+
+    def p_external_declaration_2(self, p):
+        """ external_declaration    : declaration
+        """
+        p[0] = p[1]
+
+    def p_external_declaration_3(self, p):
+        """ external_declaration    : pp_directive
+                                    | pppragma_directive
+        """
+        p[0] = [p[1]]
+
+    def p_external_declaration_4(self, p):
+        """ external_declaration    : SEMI
+        """
+        p[0] = []
+
+    def p_external_declaration_5(self, p):
+        """ external_declaration    : static_assert
+        """
+        p[0] = p[1]
+
+    def p_static_assert_declaration(self, p):
+        """ static_assert           : _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN
+                                    | _STATIC_ASSERT LPAREN constant_expression RPAREN
+        """
+        if len(p) == 5:
+            p[0] = [c_ast.StaticAssert(p[3], None, self._token_coord(p, 1))]
+        else:
+            p[0] = [c_ast.StaticAssert(p[3], p[5], self._token_coord(p, 1))]
+
+    def p_pp_directive(self, p):
+        """ pp_directive  : PPHASH
+        """
+        self._parse_error('Directives not supported yet',
+                          self._token_coord(p, 1))
+
+    # This encompasses two types of C99-compatible pragmas:
+    # - The #pragma directive:
+    #       # pragma character_sequence
+    # - The _Pragma unary operator:
+    #       _Pragma ( " string_literal " )
+    def p_pppragma_directive(self, p):
+        """ pppragma_directive      : PPPRAGMA
+                                    | PPPRAGMA PPPRAGMASTR
+                                    | _PRAGMA LPAREN unified_string_literal RPAREN
+        """
+        if len(p) == 5:
+            p[0] = c_ast.Pragma(p[3], self._token_coord(p, 2))
+        elif len(p) == 3:
+            p[0] = c_ast.Pragma(p[2], self._token_coord(p, 2))
+        else:
+            p[0] = c_ast.Pragma("", self._token_coord(p, 1))
+
+    def p_pppragma_directive_list(self, p):
+        """ pppragma_directive_list : pppragma_directive
+                                    | pppragma_directive_list pppragma_directive
+        """
+        p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]]
+
+    # In function definitions, the declarator can be followed by
+    # a declaration list, for old "K&R style" function definitios.
+    def p_function_definition_1(self, p):
+        """ function_definition : id_declarator declaration_list_opt compound_statement
+        """
+        # no declaration specifiers - 'int' becomes the default type
+        spec = dict(
+            qual=[],
+            alignment=[],
+            storage=[],
+            type=[c_ast.IdentifierType(['int'],
+                                       coord=self._token_coord(p, 1))],
+            function=[])
+
+        p[0] = self._build_function_definition(
+            spec=spec,
+            decl=p[1],
+            param_decls=p[2],
+            body=p[3])
+
+    def p_function_definition_2(self, p):
+        """ function_definition : declaration_specifiers id_declarator declaration_list_opt compound_statement
+        """
+        spec = p[1]
+
+        p[0] = self._build_function_definition(
+            spec=spec,
+            decl=p[2],
+            param_decls=p[3],
+            body=p[4])
+
+    # Note, according to C18 A.2.2 6.7.10 static_assert-declaration _Static_assert
+    # is a declaration, not a statement. We additionally recognise it as a statement
+    # to fix parsing of _Static_assert inside the functions.
+    #
+    def p_statement(self, p):
+        """ statement   : labeled_statement
+                        | expression_statement
+                        | compound_statement
+                        | selection_statement
+                        | iteration_statement
+                        | jump_statement
+                        | pppragma_directive
+                        | static_assert
+        """
+        p[0] = p[1]
+
+    # A pragma is generally considered a decorator rather than an actual
+    # statement. Still, for the purposes of analyzing an abstract syntax tree of
+    # C code, pragma's should not be ignored and were previously treated as a
+    # statement. This presents a problem for constructs that take a statement
+    # such as labeled_statements, selection_statements, and
+    # iteration_statements, causing a misleading structure in the AST. For
+    # example, consider the following C code.
+    #
+    #   for (int i = 0; i < 3; i++)
+    #       #pragma omp critical
+    #       sum += 1;
+    #
+    # This code will compile and execute "sum += 1;" as the body of the for
+    # loop. Previous implementations of PyCParser would render the AST for this
+    # block of code as follows:
+    #
+    #   For:
+    #     DeclList:
+    #       Decl: i, [], [], []
+    #         TypeDecl: i, []
+    #           IdentifierType: ['int']
+    #         Constant: int, 0
+    #     BinaryOp: <
+    #       ID: i
+    #       Constant: int, 3
+    #     UnaryOp: p++
+    #       ID: i
+    #     Pragma: omp critical
+    #   Assignment: +=
+    #     ID: sum
+    #     Constant: int, 1
+    #
+    # This AST misleadingly takes the Pragma as the body of the loop and the
+    # assignment then becomes a sibling of the loop.
+    #
+    # To solve edge cases like these, the pragmacomp_or_statement rule groups
+    # a pragma and its following statement (which would otherwise be orphaned)
+    # using a compound block, effectively turning the above code into:
+    #
+    #   for (int i = 0; i < 3; i++) {
+    #       #pragma omp critical
+    #       sum += 1;
+    #   }
+    def p_pragmacomp_or_statement(self, p):
+        """ pragmacomp_or_statement     : pppragma_directive_list statement
+                                        | statement
+        """
+        if len(p) == 3:
+            p[0] = c_ast.Compound(
+                block_items=p[1]+[p[2]],
+                coord=self._token_coord(p, 1))
+        else:
+            p[0] = p[1]
+
+    # In C, declarations can come several in a line:
+    #   int x, *px, romulo = 5;
+    #
+    # However, for the AST, we will split them to separate Decl
+    # nodes.
+    #
+    # This rule splits its declarations and always returns a list
+    # of Decl nodes, even if it's one element long.
+    #
+    def p_decl_body(self, p):
+        """ decl_body : declaration_specifiers init_declarator_list_opt
+                      | declaration_specifiers_no_type id_init_declarator_list_opt
+        """
+        spec = p[1]
+
+        # p[2] (init_declarator_list_opt) is either a list or None
+        #
+        if p[2] is None:
+            # By the standard, you must have at least one declarator unless
+            # declaring a structure tag, a union tag, or the members of an
+            # enumeration.
+            #
+            ty = spec['type']
+            s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum)
+            if len(ty) == 1 and isinstance(ty[0], s_u_or_e):
+                decls = [c_ast.Decl(
+                    name=None,
+                    quals=spec['qual'],
+                    align=spec['alignment'],
+                    storage=spec['storage'],
+                    funcspec=spec['function'],
+                    type=ty[0],
+                    init=None,
+                    bitsize=None,
+                    coord=ty[0].coord)]
+
+            # However, this case can also occur on redeclared identifiers in
+            # an inner scope.  The trouble is that the redeclared type's name
+            # gets grouped into declaration_specifiers; _build_declarations
+            # compensates for this.
+            #
+            else:
+                decls = self._build_declarations(
+                    spec=spec,
+                    decls=[dict(decl=None, init=None)],
+                    typedef_namespace=True)
+
+        else:
+            decls = self._build_declarations(
+                spec=spec,
+                decls=p[2],
+                typedef_namespace=True)
+
+        p[0] = decls
+
+    # The declaration has been split to a decl_body sub-rule and
+    # SEMI, because having them in a single rule created a problem
+    # for defining typedefs.
+    #
+    # If a typedef line was directly followed by a line using the
+    # type defined with the typedef, the type would not be
+    # recognized. This is because to reduce the declaration rule,
+    # the parser's lookahead asked for the token after SEMI, which
+    # was the type from the next line, and the lexer had no chance
+    # to see the updated type symbol table.
+    #
+    # Splitting solves this problem, because after seeing SEMI,
+    # the parser reduces decl_body, which actually adds the new
+    # type into the table to be seen by the lexer before the next
+    # line is reached.
+    def p_declaration(self, p):
+        """ declaration : decl_body SEMI
+        """
+        p[0] = p[1]
+
+    # Since each declaration is a list of declarations, this
+    # rule will combine all the declarations and return a single
+    # list
+    #
+    def p_declaration_list(self, p):
+        """ declaration_list    : declaration
+                                | declaration_list declaration
+        """
+        p[0] = p[1] if len(p) == 2 else p[1] + p[2]
+
+    # To know when declaration-specifiers end and declarators begin,
+    # we require declaration-specifiers to have at least one
+    # type-specifier, and disallow typedef-names after we've seen any
+    # type-specifier. These are both required by the spec.
+    #
+    def p_declaration_specifiers_no_type_1(self, p):
+        """ declaration_specifiers_no_type  : type_qualifier declaration_specifiers_no_type_opt
+        """
+        p[0] = self._add_declaration_specifier(p[2], p[1], 'qual')
+
+    def p_declaration_specifiers_no_type_2(self, p):
+        """ declaration_specifiers_no_type  : storage_class_specifier declaration_specifiers_no_type_opt
+        """
+        p[0] = self._add_declaration_specifier(p[2], p[1], 'storage')
+
+    def p_declaration_specifiers_no_type_3(self, p):
+        """ declaration_specifiers_no_type  : function_specifier declaration_specifiers_no_type_opt
+        """
+        p[0] = self._add_declaration_specifier(p[2], p[1], 'function')
+
+    # Without this, `typedef _Atomic(T) U` will parse incorrectly because the
+    # _Atomic qualifier will match, instead of the specifier.
+    def p_declaration_specifiers_no_type_4(self, p):
+        """ declaration_specifiers_no_type  : atomic_specifier declaration_specifiers_no_type_opt
+        """
+        p[0] = self._add_declaration_specifier(p[2], p[1], 'type')
+
+    def p_declaration_specifiers_no_type_5(self, p):
+        """ declaration_specifiers_no_type  : alignment_specifier declaration_specifiers_no_type_opt
+        """
+        p[0] = self._add_declaration_specifier(p[2], p[1], 'alignment')
+
+    def p_declaration_specifiers_1(self, p):
+        """ declaration_specifiers  : declaration_specifiers type_qualifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'qual', append=True)
+
+    def p_declaration_specifiers_2(self, p):
+        """ declaration_specifiers  : declaration_specifiers storage_class_specifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'storage', append=True)
+
+    def p_declaration_specifiers_3(self, p):
+        """ declaration_specifiers  : declaration_specifiers function_specifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'function', append=True)
+
+    def p_declaration_specifiers_4(self, p):
+        """ declaration_specifiers  : declaration_specifiers type_specifier_no_typeid
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True)
+
+    def p_declaration_specifiers_5(self, p):
+        """ declaration_specifiers  : type_specifier
+        """
+        p[0] = self._add_declaration_specifier(None, p[1], 'type')
+
+    def p_declaration_specifiers_6(self, p):
+        """ declaration_specifiers  : declaration_specifiers_no_type type_specifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True)
+
+    def p_declaration_specifiers_7(self, p):
+        """ declaration_specifiers  : declaration_specifiers alignment_specifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'alignment', append=True)
+
+    def p_storage_class_specifier(self, p):
+        """ storage_class_specifier : AUTO
+                                    | REGISTER
+                                    | STATIC
+                                    | EXTERN
+                                    | TYPEDEF
+                                    | _THREAD_LOCAL
+        """
+        p[0] = p[1]
+
+    def p_function_specifier(self, p):
+        """ function_specifier  : INLINE
+                                | _NORETURN
+        """
+        p[0] = p[1]
+
+    def p_type_specifier_no_typeid(self, p):
+        """ type_specifier_no_typeid  : VOID
+                                      | _BOOL
+                                      | CHAR
+                                      | SHORT
+                                      | INT
+                                      | LONG
+                                      | FLOAT
+                                      | DOUBLE
+                                      | _COMPLEX
+                                      | SIGNED
+                                      | UNSIGNED
+                                      | __INT128
+        """
+        p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1))
+
+    def p_type_specifier(self, p):
+        """ type_specifier  : typedef_name
+                            | enum_specifier
+                            | struct_or_union_specifier
+                            | type_specifier_no_typeid
+                            | atomic_specifier
+        """
+        p[0] = p[1]
+
+    # See section 6.7.2.4 of the C11 standard.
+    def p_atomic_specifier(self, p):
+        """ atomic_specifier  : _ATOMIC LPAREN type_name RPAREN
+        """
+        typ = p[3]
+        typ.quals.append('_Atomic')
+        p[0] = typ
+
+    def p_type_qualifier(self, p):
+        """ type_qualifier  : CONST
+                            | RESTRICT
+                            | VOLATILE
+                            | _ATOMIC
+        """
+        p[0] = p[1]
+
+    def p_init_declarator_list(self, p):
+        """ init_declarator_list    : init_declarator
+                                    | init_declarator_list COMMA init_declarator
+        """
+        p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]]
+
+    # Returns a {decl=<declarator> : init=<initializer>} dictionary
+    # If there's no initializer, uses None
+    #
+    def p_init_declarator(self, p):
+        """ init_declarator : declarator
+                            | declarator EQUALS initializer
+        """
+        p[0] = dict(decl=p[1], init=(p[3] if len(p) > 2 else None))
+
+    def p_id_init_declarator_list(self, p):
+        """ id_init_declarator_list    : id_init_declarator
+                                       | id_init_declarator_list COMMA init_declarator
+        """
+        p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]]
+
+    def p_id_init_declarator(self, p):
+        """ id_init_declarator : id_declarator
+                               | id_declarator EQUALS initializer
+        """
+        p[0] = dict(decl=p[1], init=(p[3] if len(p) > 2 else None))
+
+    # Require at least one type specifier in a specifier-qualifier-list
+    #
+    def p_specifier_qualifier_list_1(self, p):
+        """ specifier_qualifier_list    : specifier_qualifier_list type_specifier_no_typeid
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True)
+
+    def p_specifier_qualifier_list_2(self, p):
+        """ specifier_qualifier_list    : specifier_qualifier_list type_qualifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'qual', append=True)
+
+    def p_specifier_qualifier_list_3(self, p):
+        """ specifier_qualifier_list  : type_specifier
+        """
+        p[0] = self._add_declaration_specifier(None, p[1], 'type')
+
+    def p_specifier_qualifier_list_4(self, p):
+        """ specifier_qualifier_list  : type_qualifier_list type_specifier
+        """
+        p[0] = dict(qual=p[1], alignment=[], storage=[], type=[p[2]], function=[])
+
+    def p_specifier_qualifier_list_5(self, p):
+        """ specifier_qualifier_list  : alignment_specifier
+        """
+        p[0] = dict(qual=[], alignment=[p[1]], storage=[], type=[], function=[])
+
+    def p_specifier_qualifier_list_6(self, p):
+        """ specifier_qualifier_list  : specifier_qualifier_list alignment_specifier
+        """
+        p[0] = self._add_declaration_specifier(p[1], p[2], 'alignment')
+
+    # TYPEID is allowed here (and in other struct/enum related tag names), because
+    # struct/enum tags reside in their own namespace and can be named the same as types
+    #
+    def p_struct_or_union_specifier_1(self, p):
+        """ struct_or_union_specifier   : struct_or_union ID
+                                        | struct_or_union TYPEID
+        """
+        klass = self._select_struct_union_class(p[1])
+        # None means no list of members
+        p[0] = klass(
+            name=p[2],
+            decls=None,
+            coord=self._token_coord(p, 2))
+
+    def p_struct_or_union_specifier_2(self, p):
+        """ struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close
+                                      | struct_or_union brace_open brace_close
+        """
+        klass = self._select_struct_union_class(p[1])
+        if len(p) == 4:
+            # Empty sequence means an empty list of members
+            p[0] = klass(
+                name=None,
+                decls=[],
+                coord=self._token_coord(p, 2))
+        else:
+            p[0] = klass(
+                name=None,
+                decls=p[3],
+                coord=self._token_coord(p, 2))
+
+
+    def p_struct_or_union_specifier_3(self, p):
+        """ struct_or_union_specifier   : struct_or_union ID brace_open struct_declaration_list brace_close
+                                        | struct_or_union ID brace_open brace_close
+                                        | struct_or_union TYPEID brace_open struct_declaration_list brace_close
+                                        | struct_or_union TYPEID brace_open brace_close
+        """
+        klass = self._select_struct_union_class(p[1])
+        if len(p) == 5:
+            # Empty sequence means an empty list of members
+            p[0] = klass(
+                name=p[2],
+                decls=[],
+                coord=self._token_coord(p, 2))
+        else:
+            p[0] = klass(
+                name=p[2],
+                decls=p[4],
+                coord=self._token_coord(p, 2))
+
+    def p_struct_or_union(self, p):
+        """ struct_or_union : STRUCT
+                            | UNION
+        """
+        p[0] = p[1]
+
+    # Combine all declarations into a single list
+    #
+    def p_struct_declaration_list(self, p):
+        """ struct_declaration_list     : struct_declaration
+                                        | struct_declaration_list struct_declaration
+        """
+        if len(p) == 2:
+            p[0] = p[1] or []
+        else:
+            p[0] = p[1] + (p[2] or [])
+
+    def p_struct_declaration_1(self, p):
+        """ struct_declaration : specifier_qualifier_list struct_declarator_list_opt SEMI
+        """
+        spec = p[1]
+        assert 'typedef' not in spec['storage']
+
+        if p[2] is not None:
+            decls = self._build_declarations(
+                spec=spec,
+                decls=p[2])
+
+        elif len(spec['type']) == 1:
+            # Anonymous struct/union, gcc extension, C1x feature.
+            # Although the standard only allows structs/unions here, I see no
+            # reason to disallow other types since some compilers have typedefs
+            # here, and pycparser isn't about rejecting all invalid code.
+            #
+            node = spec['type'][0]
+            if isinstance(node, c_ast.Node):
+                decl_type = node
+            else:
+                decl_type = c_ast.IdentifierType(node)
+
+            decls = self._build_declarations(
+                spec=spec,
+                decls=[dict(decl=decl_type)])
+
+        else:
+            # Structure/union members can have the same names as typedefs.
+            # The trouble is that the member's name gets grouped into
+            # specifier_qualifier_list; _build_declarations compensates.
+            #
+            decls = self._build_declarations(
+                spec=spec,
+                decls=[dict(decl=None, init=None)])
+
+        p[0] = decls
+
+    def p_struct_declaration_2(self, p):
+        """ struct_declaration : SEMI
+        """
+        p[0] = None
+
+    def p_struct_declaration_3(self, p):
+        """ struct_declaration : pppragma_directive
+        """
+        p[0] = [p[1]]
+
+    def p_struct_declarator_list(self, p):
+        """ struct_declarator_list  : struct_declarator
+                                    | struct_declarator_list COMMA struct_declarator
+        """
+        p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]]
+
+    # struct_declarator passes up a dict with the keys: decl (for
+    # the underlying declarator) and bitsize (for the bitsize)
+    #
+    def p_struct_declarator_1(self, p):
+        """ struct_declarator : declarator
+        """
+        p[0] = {'decl': p[1], 'bitsize': None}
+
+    def p_struct_declarator_2(self, p):
+        """ struct_declarator   : declarator COLON constant_expression
+                                | COLON constant_expression
+        """
+        if len(p) > 3:
+            p[0] = {'decl': p[1], 'bitsize': p[3]}
+        else:
+            p[0] = {'decl': c_ast.TypeDecl(None, None, None, None), 'bitsize': p[2]}
+
+    def p_enum_specifier_1(self, p):
+        """ enum_specifier  : ENUM ID
+                            | ENUM TYPEID
+        """
+        p[0] = c_ast.Enum(p[2], None, self._token_coord(p, 1))
+
+    def p_enum_specifier_2(self, p):
+        """ enum_specifier  : ENUM brace_open enumerator_list brace_close
+        """
+        p[0] = c_ast.Enum(None, p[3], self._token_coord(p, 1))
+
+    def p_enum_specifier_3(self, p):
+        """ enum_specifier  : ENUM ID brace_open enumerator_list brace_close
+                            | ENUM TYPEID brace_open enumerator_list brace_close
+        """
+        p[0] = c_ast.Enum(p[2], p[4], self._token_coord(p, 1))
+
+    def p_enumerator_list(self, p):
+        """ enumerator_list : enumerator
+                            | enumerator_list COMMA
+                            | enumerator_list COMMA enumerator
+        """
+        if len(p) == 2:
+            p[0] = c_ast.EnumeratorList([p[1]], p[1].coord)
+        elif len(p) == 3:
+            p[0] = p[1]
+        else:
+            p[1].enumerators.append(p[3])
+            p[0] = p[1]
+
+    def p_alignment_specifier(self, p):
+        """ alignment_specifier  : _ALIGNAS LPAREN type_name RPAREN
+                                 | _ALIGNAS LPAREN constant_expression RPAREN
+        """
+        p[0] = c_ast.Alignas(p[3], self._token_coord(p, 1))
+
+    def p_enumerator(self, p):
+        """ enumerator  : ID
+                        | ID EQUALS constant_expression
+        """
+        if len(p) == 2:
+            enumerator = c_ast.Enumerator(
+                        p[1], None,
+                        self._token_coord(p, 1))
+        else:
+            enumerator = c_ast.Enumerator(
+                        p[1], p[3],
+                        self._token_coord(p, 1))
+        self._add_identifier(enumerator.name, enumerator.coord)
+
+        p[0] = enumerator
+
+    def p_declarator(self, p):
+        """ declarator  : id_declarator
+                        | typeid_declarator
+        """
+        p[0] = p[1]
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_xxx_declarator_1(self, p):
+        """ xxx_declarator  : direct_xxx_declarator
+        """
+        p[0] = p[1]
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_xxx_declarator_2(self, p):
+        """ xxx_declarator  : pointer direct_xxx_declarator
+        """
+        p[0] = self._type_modify_decl(p[2], p[1])
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_direct_xxx_declarator_1(self, p):
+        """ direct_xxx_declarator   : yyy
+        """
+        p[0] = c_ast.TypeDecl(
+            declname=p[1],
+            type=None,
+            quals=None,
+            align=None,
+            coord=self._token_coord(p, 1))
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'))
+    def p_direct_xxx_declarator_2(self, p):
+        """ direct_xxx_declarator   : LPAREN xxx_declarator RPAREN
+        """
+        p[0] = p[2]
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_direct_xxx_declarator_3(self, p):
+        """ direct_xxx_declarator   : direct_xxx_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET
+        """
+        quals = (p[3] if len(p) > 5 else []) or []
+        # Accept dimension qualifiers
+        # Per C99 6.7.5.3 p7
+        arr = c_ast.ArrayDecl(
+            type=None,
+            dim=p[4] if len(p) > 5 else p[3],
+            dim_quals=quals,
+            coord=p[1].coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=arr)
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_direct_xxx_declarator_4(self, p):
+        """ direct_xxx_declarator   : direct_xxx_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET
+                                    | direct_xxx_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET
+        """
+        # Using slice notation for PLY objects doesn't work in Python 3 for the
+        # version of PLY embedded with pycparser; see PLY Google Code issue 30.
+        # Work around that here by listing the two elements separately.
+        listed_quals = [item if isinstance(item, list) else [item]
+            for item in [p[3],p[4]]]
+        dim_quals = [qual for sublist in listed_quals for qual in sublist
+            if qual is not None]
+        arr = c_ast.ArrayDecl(
+            type=None,
+            dim=p[5],
+            dim_quals=dim_quals,
+            coord=p[1].coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=arr)
+
+    # Special for VLAs
+    #
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_direct_xxx_declarator_5(self, p):
+        """ direct_xxx_declarator   : direct_xxx_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET
+        """
+        arr = c_ast.ArrayDecl(
+            type=None,
+            dim=c_ast.ID(p[4], self._token_coord(p, 4)),
+            dim_quals=p[3] if p[3] is not None else [],
+            coord=p[1].coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=arr)
+
+    @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID'))
+    def p_direct_xxx_declarator_6(self, p):
+        """ direct_xxx_declarator   : direct_xxx_declarator LPAREN parameter_type_list RPAREN
+                                    | direct_xxx_declarator LPAREN identifier_list_opt RPAREN
+        """
+        func = c_ast.FuncDecl(
+            args=p[3],
+            type=None,
+            coord=p[1].coord)
+
+        # To see why _get_yacc_lookahead_token is needed, consider:
+        #   typedef char TT;
+        #   void foo(int TT) { TT = 10; }
+        # Outside the function, TT is a typedef, but inside (starting and
+        # ending with the braces) it's a parameter.  The trouble begins with
+        # yacc's lookahead token.  We don't know if we're declaring or
+        # defining a function until we see LBRACE, but if we wait for yacc to
+        # trigger a rule on that token, then TT will have already been read
+        # and incorrectly interpreted as TYPEID.  We need to add the
+        # parameters to the scope the moment the lexer sees LBRACE.
+        #
+        if self._get_yacc_lookahead_token().type == "LBRACE":
+            if func.args is not None:
+                for param in func.args.params:
+                    if isinstance(param, c_ast.EllipsisParam): break
+                    self._add_identifier(param.name, param.coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=func)
+
+    def p_pointer(self, p):
+        """ pointer : TIMES type_qualifier_list_opt
+                    | TIMES type_qualifier_list_opt pointer
+        """
+        coord = self._token_coord(p, 1)
+        # Pointer decls nest from inside out. This is important when different
+        # levels have different qualifiers. For example:
+        #
+        #  char * const * p;
+        #
+        # Means "pointer to const pointer to char"
+        #
+        # While:
+        #
+        #  char ** const p;
+        #
+        # Means "const pointer to pointer to char"
+        #
+        # So when we construct PtrDecl nestings, the leftmost pointer goes in
+        # as the most nested type.
+        nested_type = c_ast.PtrDecl(quals=p[2] or [], type=None, coord=coord)
+        if len(p) > 3:
+            tail_type = p[3]
+            while tail_type.type is not None:
+                tail_type = tail_type.type
+            tail_type.type = nested_type
+            p[0] = p[3]
+        else:
+            p[0] = nested_type
+
+    def p_type_qualifier_list(self, p):
+        """ type_qualifier_list : type_qualifier
+                                | type_qualifier_list type_qualifier
+        """
+        p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]]
+
+    def p_parameter_type_list(self, p):
+        """ parameter_type_list : parameter_list
+                                | parameter_list COMMA ELLIPSIS
+        """
+        if len(p) > 2:
+            p[1].params.append(c_ast.EllipsisParam(self._token_coord(p, 3)))
+
+        p[0] = p[1]
+
+    def p_parameter_list(self, p):
+        """ parameter_list  : parameter_declaration
+                            | parameter_list COMMA parameter_declaration
+        """
+        if len(p) == 2: # single parameter
+            p[0] = c_ast.ParamList([p[1]], p[1].coord)
+        else:
+            p[1].params.append(p[3])
+            p[0] = p[1]
+
+    # From ISO/IEC 9899:TC2, 6.7.5.3.11:
+    # "If, in a parameter declaration, an identifier can be treated either
+    #  as a typedef name or as a parameter name, it shall be taken as a
+    #  typedef name."
+    #
+    # Inside a parameter declaration, once we've reduced declaration specifiers,
+    # if we shift in an LPAREN and see a TYPEID, it could be either an abstract
+    # declarator or a declarator nested inside parens. This rule tells us to
+    # always treat it as an abstract declarator. Therefore, we only accept
+    # `id_declarator`s and `typeid_noparen_declarator`s.
+    def p_parameter_declaration_1(self, p):
+        """ parameter_declaration   : declaration_specifiers id_declarator
+                                    | declaration_specifiers typeid_noparen_declarator
+        """
+        spec = p[1]
+        if not spec['type']:
+            spec['type'] = [c_ast.IdentifierType(['int'],
+                coord=self._token_coord(p, 1))]
+        p[0] = self._build_declarations(
+            spec=spec,
+            decls=[dict(decl=p[2])])[0]
+
+    def p_parameter_declaration_2(self, p):
+        """ parameter_declaration   : declaration_specifiers abstract_declarator_opt
+        """
+        spec = p[1]
+        if not spec['type']:
+            spec['type'] = [c_ast.IdentifierType(['int'],
+                coord=self._token_coord(p, 1))]
+
+        # Parameters can have the same names as typedefs.  The trouble is that
+        # the parameter's name gets grouped into declaration_specifiers, making
+        # it look like an old-style declaration; compensate.
+        #
+        if len(spec['type']) > 1 and len(spec['type'][-1].names) == 1 and \
+                self._is_type_in_scope(spec['type'][-1].names[0]):
+            decl = self._build_declarations(
+                    spec=spec,
+                    decls=[dict(decl=p[2], init=None)])[0]
+
+        # This truly is an old-style parameter declaration
+        #
+        else:
+            decl = c_ast.Typename(
+                name='',
+                quals=spec['qual'],
+                align=None,
+                type=p[2] or c_ast.TypeDecl(None, None, None, None),
+                coord=self._token_coord(p, 2))
+            typename = spec['type']
+            decl = self._fix_decl_name_type(decl, typename)
+
+        p[0] = decl
+
+    def p_identifier_list(self, p):
+        """ identifier_list : identifier
+                            | identifier_list COMMA identifier
+        """
+        if len(p) == 2: # single parameter
+            p[0] = c_ast.ParamList([p[1]], p[1].coord)
+        else:
+            p[1].params.append(p[3])
+            p[0] = p[1]
+
+    def p_initializer_1(self, p):
+        """ initializer : assignment_expression
+        """
+        p[0] = p[1]
+
+    def p_initializer_2(self, p):
+        """ initializer : brace_open initializer_list_opt brace_close
+                        | brace_open initializer_list COMMA brace_close
+        """
+        if p[2] is None:
+            p[0] = c_ast.InitList([], self._token_coord(p, 1))
+        else:
+            p[0] = p[2]
+
+    def p_initializer_list(self, p):
+        """ initializer_list    : designation_opt initializer
+                                | initializer_list COMMA designation_opt initializer
+        """
+        if len(p) == 3: # single initializer
+            init = p[2] if p[1] is None else c_ast.NamedInitializer(p[1], p[2])
+            p[0] = c_ast.InitList([init], p[2].coord)
+        else:
+            init = p[4] if p[3] is None else c_ast.NamedInitializer(p[3], p[4])
+            p[1].exprs.append(init)
+            p[0] = p[1]
+
+    def p_designation(self, p):
+        """ designation : designator_list EQUALS
+        """
+        p[0] = p[1]
+
+    # Designators are represented as a list of nodes, in the order in which
+    # they're written in the code.
+    #
+    def p_designator_list(self, p):
+        """ designator_list : designator
+                            | designator_list designator
+        """
+        p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]]
+
+    def p_designator(self, p):
+        """ designator  : LBRACKET constant_expression RBRACKET
+                        | PERIOD identifier
+        """
+        p[0] = p[2]
+
+    def p_type_name(self, p):
+        """ type_name   : specifier_qualifier_list abstract_declarator_opt
+        """
+        typename = c_ast.Typename(
+            name='',
+            quals=p[1]['qual'][:],
+            align=None,
+            type=p[2] or c_ast.TypeDecl(None, None, None, None),
+            coord=self._token_coord(p, 2))
+
+        p[0] = self._fix_decl_name_type(typename, p[1]['type'])
+
+    def p_abstract_declarator_1(self, p):
+        """ abstract_declarator     : pointer
+        """
+        dummytype = c_ast.TypeDecl(None, None, None, None)
+        p[0] = self._type_modify_decl(
+            decl=dummytype,
+            modifier=p[1])
+
+    def p_abstract_declarator_2(self, p):
+        """ abstract_declarator     : pointer direct_abstract_declarator
+        """
+        p[0] = self._type_modify_decl(p[2], p[1])
+
+    def p_abstract_declarator_3(self, p):
+        """ abstract_declarator     : direct_abstract_declarator
+        """
+        p[0] = p[1]
+
+    # Creating and using direct_abstract_declarator_opt here
+    # instead of listing both direct_abstract_declarator and the
+    # lack of it in the beginning of _1 and _2 caused two
+    # shift/reduce errors.
+    #
+    def p_direct_abstract_declarator_1(self, p):
+        """ direct_abstract_declarator  : LPAREN abstract_declarator RPAREN """
+        p[0] = p[2]
+
+    def p_direct_abstract_declarator_2(self, p):
+        """ direct_abstract_declarator  : direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET
+        """
+        arr = c_ast.ArrayDecl(
+            type=None,
+            dim=p[3],
+            dim_quals=[],
+            coord=p[1].coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=arr)
+
+    def p_direct_abstract_declarator_3(self, p):
+        """ direct_abstract_declarator  : LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET
+        """
+        quals = (p[2] if len(p) > 4 else []) or []
+        p[0] = c_ast.ArrayDecl(
+            type=c_ast.TypeDecl(None, None, None, None),
+            dim=p[3] if len(p) > 4 else p[2],
+            dim_quals=quals,
+            coord=self._token_coord(p, 1))
+
+    def p_direct_abstract_declarator_4(self, p):
+        """ direct_abstract_declarator  : direct_abstract_declarator LBRACKET TIMES RBRACKET
+        """
+        arr = c_ast.ArrayDecl(
+            type=None,
+            dim=c_ast.ID(p[3], self._token_coord(p, 3)),
+            dim_quals=[],
+            coord=p[1].coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=arr)
+
+    def p_direct_abstract_declarator_5(self, p):
+        """ direct_abstract_declarator  : LBRACKET TIMES RBRACKET
+        """
+        p[0] = c_ast.ArrayDecl(
+            type=c_ast.TypeDecl(None, None, None, None),
+            dim=c_ast.ID(p[3], self._token_coord(p, 3)),
+            dim_quals=[],
+            coord=self._token_coord(p, 1))
+
+    def p_direct_abstract_declarator_6(self, p):
+        """ direct_abstract_declarator  : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN
+        """
+        func = c_ast.FuncDecl(
+            args=p[3],
+            type=None,
+            coord=p[1].coord)
+
+        p[0] = self._type_modify_decl(decl=p[1], modifier=func)
+
+    def p_direct_abstract_declarator_7(self, p):
+        """ direct_abstract_declarator  : LPAREN parameter_type_list_opt RPAREN
+        """
+        p[0] = c_ast.FuncDecl(
+            args=p[2],
+            type=c_ast.TypeDecl(None, None, None, None),
+            coord=self._token_coord(p, 1))
+
+    # declaration is a list, statement isn't. To make it consistent, block_item
+    # will always be a list
+    #
+    def p_block_item(self, p):
+        """ block_item  : declaration
+                        | statement
+        """
+        p[0] = p[1] if isinstance(p[1], list) else [p[1]]
+
+    # Since we made block_item a list, this just combines lists
+    #
+    def p_block_item_list(self, p):
+        """ block_item_list : block_item
+                            | block_item_list block_item
+        """
+        # Empty block items (plain ';') produce [None], so ignore them
+        p[0] = p[1] if (len(p) == 2 or p[2] == [None]) else p[1] + p[2]
+
+    def p_compound_statement_1(self, p):
+        """ compound_statement : brace_open block_item_list_opt brace_close """
+        p[0] = c_ast.Compound(
+            block_items=p[2],
+            coord=self._token_coord(p, 1))
+
+    def p_labeled_statement_1(self, p):
+        """ labeled_statement : ID COLON pragmacomp_or_statement """
+        p[0] = c_ast.Label(p[1], p[3], self._token_coord(p, 1))
+
+    def p_labeled_statement_2(self, p):
+        """ labeled_statement : CASE constant_expression COLON pragmacomp_or_statement """
+        p[0] = c_ast.Case(p[2], [p[4]], self._token_coord(p, 1))
+
+    def p_labeled_statement_3(self, p):
+        """ labeled_statement : DEFAULT COLON pragmacomp_or_statement """
+        p[0] = c_ast.Default([p[3]], self._token_coord(p, 1))
+
+    def p_selection_statement_1(self, p):
+        """ selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement """
+        p[0] = c_ast.If(p[3], p[5], None, self._token_coord(p, 1))
+
+    def p_selection_statement_2(self, p):
+        """ selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement """
+        p[0] = c_ast.If(p[3], p[5], p[7], self._token_coord(p, 1))
+
+    def p_selection_statement_3(self, p):
+        """ selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement """
+        p[0] = fix_switch_cases(
+                c_ast.Switch(p[3], p[5], self._token_coord(p, 1)))
+
+    def p_iteration_statement_1(self, p):
+        """ iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement """
+        p[0] = c_ast.While(p[3], p[5], self._token_coord(p, 1))
+
+    def p_iteration_statement_2(self, p):
+        """ iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI """
+        p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1))
+
+    def p_iteration_statement_3(self, p):
+        """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """
+        p[0] = c_ast.For(p[3], p[5], p[7], p[9], self._token_coord(p, 1))
+
+    def p_iteration_statement_4(self, p):
+        """ iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """
+        p[0] = c_ast.For(c_ast.DeclList(p[3], self._token_coord(p, 1)),
+                         p[4], p[6], p[8], self._token_coord(p, 1))
+
+    def p_jump_statement_1(self, p):
+        """ jump_statement  : GOTO ID SEMI """
+        p[0] = c_ast.Goto(p[2], self._token_coord(p, 1))
+
+    def p_jump_statement_2(self, p):
+        """ jump_statement  : BREAK SEMI """
+        p[0] = c_ast.Break(self._token_coord(p, 1))
+
+    def p_jump_statement_3(self, p):
+        """ jump_statement  : CONTINUE SEMI """
+        p[0] = c_ast.Continue(self._token_coord(p, 1))
+
+    def p_jump_statement_4(self, p):
+        """ jump_statement  : RETURN expression SEMI
+                            | RETURN SEMI
+        """
+        p[0] = c_ast.Return(p[2] if len(p) == 4 else None, self._token_coord(p, 1))
+
+    def p_expression_statement(self, p):
+        """ expression_statement : expression_opt SEMI """
+        if p[1] is None:
+            p[0] = c_ast.EmptyStatement(self._token_coord(p, 2))
+        else:
+            p[0] = p[1]
+
+    def p_expression(self, p):
+        """ expression  : assignment_expression
+                        | expression COMMA assignment_expression
+        """
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            if not isinstance(p[1], c_ast.ExprList):
+                p[1] = c_ast.ExprList([p[1]], p[1].coord)
+
+            p[1].exprs.append(p[3])
+            p[0] = p[1]
+
+    def p_parenthesized_compound_expression(self, p):
+        """ assignment_expression : LPAREN compound_statement RPAREN """
+        p[0] = p[2]
+
+    def p_typedef_name(self, p):
+        """ typedef_name : TYPEID """
+        p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1))
+
+    def p_assignment_expression(self, p):
+        """ assignment_expression   : conditional_expression
+                                    | unary_expression assignment_operator assignment_expression
+        """
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            p[0] = c_ast.Assignment(p[2], p[1], p[3], p[1].coord)
+
+    # K&R2 defines these as many separate rules, to encode
+    # precedence and associativity. Why work hard ? I'll just use
+    # the built in precedence/associativity specification feature
+    # of PLY. (see precedence declaration above)
+    #
+    def p_assignment_operator(self, p):
+        """ assignment_operator : EQUALS
+                                | XOREQUAL
+                                | TIMESEQUAL
+                                | DIVEQUAL
+                                | MODEQUAL
+                                | PLUSEQUAL
+                                | MINUSEQUAL
+                                | LSHIFTEQUAL
+                                | RSHIFTEQUAL
+                                | ANDEQUAL
+                                | OREQUAL
+        """
+        p[0] = p[1]
+
+    def p_constant_expression(self, p):
+        """ constant_expression : conditional_expression """
+        p[0] = p[1]
+
+    def p_conditional_expression(self, p):
+        """ conditional_expression  : binary_expression
+                                    | binary_expression CONDOP expression COLON conditional_expression
+        """
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            p[0] = c_ast.TernaryOp(p[1], p[3], p[5], p[1].coord)
+
+    def p_binary_expression(self, p):
+        """ binary_expression   : cast_expression
+                                | binary_expression TIMES binary_expression
+                                | binary_expression DIVIDE binary_expression
+                                | binary_expression MOD binary_expression
+                                | binary_expression PLUS binary_expression
+                                | binary_expression MINUS binary_expression
+                                | binary_expression RSHIFT binary_expression
+                                | binary_expression LSHIFT binary_expression
+                                | binary_expression LT binary_expression
+                                | binary_expression LE binary_expression
+                                | binary_expression GE binary_expression
+                                | binary_expression GT binary_expression
+                                | binary_expression EQ binary_expression
+                                | binary_expression NE binary_expression
+                                | binary_expression AND binary_expression
+                                | binary_expression OR binary_expression
+                                | binary_expression XOR binary_expression
+                                | binary_expression LAND binary_expression
+                                | binary_expression LOR binary_expression
+        """
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            p[0] = c_ast.BinaryOp(p[2], p[1], p[3], p[1].coord)
+
+    def p_cast_expression_1(self, p):
+        """ cast_expression : unary_expression """
+        p[0] = p[1]
+
+    def p_cast_expression_2(self, p):
+        """ cast_expression : LPAREN type_name RPAREN cast_expression """
+        p[0] = c_ast.Cast(p[2], p[4], self._token_coord(p, 1))
+
+    def p_unary_expression_1(self, p):
+        """ unary_expression    : postfix_expression """
+        p[0] = p[1]
+
+    def p_unary_expression_2(self, p):
+        """ unary_expression    : PLUSPLUS unary_expression
+                                | MINUSMINUS unary_expression
+                                | unary_operator cast_expression
+        """
+        p[0] = c_ast.UnaryOp(p[1], p[2], p[2].coord)
+
+    def p_unary_expression_3(self, p):
+        """ unary_expression    : SIZEOF unary_expression
+                                | SIZEOF LPAREN type_name RPAREN
+                                | _ALIGNOF LPAREN type_name RPAREN
+        """
+        p[0] = c_ast.UnaryOp(
+            p[1],
+            p[2] if len(p) == 3 else p[3],
+            self._token_coord(p, 1))
+
+    def p_unary_operator(self, p):
+        """ unary_operator  : AND
+                            | TIMES
+                            | PLUS
+                            | MINUS
+                            | NOT
+                            | LNOT
+        """
+        p[0] = p[1]
+
+    def p_postfix_expression_1(self, p):
+        """ postfix_expression  : primary_expression """
+        p[0] = p[1]
+
+    def p_postfix_expression_2(self, p):
+        """ postfix_expression  : postfix_expression LBRACKET expression RBRACKET """
+        p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord)
+
+    def p_postfix_expression_3(self, p):
+        """ postfix_expression  : postfix_expression LPAREN argument_expression_list RPAREN
+                                | postfix_expression LPAREN RPAREN
+        """
+        p[0] = c_ast.FuncCall(p[1], p[3] if len(p) == 5 else None, p[1].coord)
+
+    def p_postfix_expression_4(self, p):
+        """ postfix_expression  : postfix_expression PERIOD ID
+                                | postfix_expression PERIOD TYPEID
+                                | postfix_expression ARROW ID
+                                | postfix_expression ARROW TYPEID
+        """
+        field = c_ast.ID(p[3], self._token_coord(p, 3))
+        p[0] = c_ast.StructRef(p[1], p[2], field, p[1].coord)
+
+    def p_postfix_expression_5(self, p):
+        """ postfix_expression  : postfix_expression PLUSPLUS
+                                | postfix_expression MINUSMINUS
+        """
+        p[0] = c_ast.UnaryOp('p' + p[2], p[1], p[1].coord)
+
+    def p_postfix_expression_6(self, p):
+        """ postfix_expression  : LPAREN type_name RPAREN brace_open initializer_list brace_close
+                                | LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close
+        """
+        p[0] = c_ast.CompoundLiteral(p[2], p[5])
+
+    def p_primary_expression_1(self, p):
+        """ primary_expression  : identifier """
+        p[0] = p[1]
+
+    def p_primary_expression_2(self, p):
+        """ primary_expression  : constant """
+        p[0] = p[1]
+
+    def p_primary_expression_3(self, p):
+        """ primary_expression  : unified_string_literal
+                                | unified_wstring_literal
+        """
+        p[0] = p[1]
+
+    def p_primary_expression_4(self, p):
+        """ primary_expression  : LPAREN expression RPAREN """
+        p[0] = p[2]
+
+    def p_primary_expression_5(self, p):
+        """ primary_expression  : OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN
+        """
+        coord = self._token_coord(p, 1)
+        p[0] = c_ast.FuncCall(c_ast.ID(p[1], coord),
+                              c_ast.ExprList([p[3], p[5]], coord),
+                              coord)
+
+    def p_offsetof_member_designator(self, p):
+        """ offsetof_member_designator : identifier
+                                         | offsetof_member_designator PERIOD identifier
+                                         | offsetof_member_designator LBRACKET expression RBRACKET
+        """
+        if len(p) == 2:
+            p[0] = p[1]
+        elif len(p) == 4:
+            p[0] = c_ast.StructRef(p[1], p[2], p[3], p[1].coord)
+        elif len(p) == 5:
+            p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord)
+        else:
+            raise NotImplementedError("Unexpected parsing state. len(p): %u" % len(p))
+
+    def p_argument_expression_list(self, p):
+        """ argument_expression_list    : assignment_expression
+                                        | argument_expression_list COMMA assignment_expression
+        """
+        if len(p) == 2: # single expr
+            p[0] = c_ast.ExprList([p[1]], p[1].coord)
+        else:
+            p[1].exprs.append(p[3])
+            p[0] = p[1]
+
+    def p_identifier(self, p):
+        """ identifier  : ID """
+        p[0] = c_ast.ID(p[1], self._token_coord(p, 1))
+
+    def p_constant_1(self, p):
+        """ constant    : INT_CONST_DEC
+                        | INT_CONST_OCT
+                        | INT_CONST_HEX
+                        | INT_CONST_BIN
+                        | INT_CONST_CHAR
+        """
+        uCount = 0
+        lCount = 0
+        for x in p[1][-3:]:
+            if x in ('l', 'L'):
+                lCount += 1
+            elif x in ('u', 'U'):
+                uCount += 1
+        t = ''
+        if uCount > 1:
+             raise ValueError('Constant cannot have more than one u/U suffix.')
+        elif lCount > 2:
+             raise ValueError('Constant cannot have more than two l/L suffix.')
+        prefix = 'unsigned ' * uCount + 'long ' * lCount
+        p[0] = c_ast.Constant(
+            prefix + 'int', p[1], self._token_coord(p, 1))
+
+    def p_constant_2(self, p):
+        """ constant    : FLOAT_CONST
+                        | HEX_FLOAT_CONST
+        """
+        if 'x' in p[1].lower():
+            t = 'float'
+        else:
+            if p[1][-1] in ('f', 'F'):
+                t = 'float'
+            elif p[1][-1] in ('l', 'L'):
+                t = 'long double'
+            else:
+                t = 'double'
+
+        p[0] = c_ast.Constant(
+            t, p[1], self._token_coord(p, 1))
+
+    def p_constant_3(self, p):
+        """ constant    : CHAR_CONST
+                        | WCHAR_CONST
+                        | U8CHAR_CONST
+                        | U16CHAR_CONST
+                        | U32CHAR_CONST
+        """
+        p[0] = c_ast.Constant(
+            'char', p[1], self._token_coord(p, 1))
+
+    # The "unified" string and wstring literal rules are for supporting
+    # concatenation of adjacent string literals.
+    # I.e. "hello " "world" is seen by the C compiler as a single string literal
+    # with the value "hello world"
+    #
+    def p_unified_string_literal(self, p):
+        """ unified_string_literal  : STRING_LITERAL
+                                    | unified_string_literal STRING_LITERAL
+        """
+        if len(p) == 2: # single literal
+            p[0] = c_ast.Constant(
+                'string', p[1], self._token_coord(p, 1))
+        else:
+            p[1].value = p[1].value[:-1] + p[2][1:]
+            p[0] = p[1]
+
+    def p_unified_wstring_literal(self, p):
+        """ unified_wstring_literal : WSTRING_LITERAL
+                                    | U8STRING_LITERAL
+                                    | U16STRING_LITERAL
+                                    | U32STRING_LITERAL
+                                    | unified_wstring_literal WSTRING_LITERAL
+                                    | unified_wstring_literal U8STRING_LITERAL
+                                    | unified_wstring_literal U16STRING_LITERAL
+                                    | unified_wstring_literal U32STRING_LITERAL
+        """
+        if len(p) == 2: # single literal
+            p[0] = c_ast.Constant(
+                'string', p[1], self._token_coord(p, 1))
+        else:
+            p[1].value = p[1].value.rstrip()[:-1] + p[2][2:]
+            p[0] = p[1]
+
+    def p_brace_open(self, p):
+        """ brace_open  :   LBRACE
+        """
+        p[0] = p[1]
+        p.set_lineno(0, p.lineno(1))
+
+    def p_brace_close(self, p):
+        """ brace_close :   RBRACE
+        """
+        p[0] = p[1]
+        p.set_lineno(0, p.lineno(1))
+
+    def p_empty(self, p):
+        'empty : '
+        p[0] = None
+
+    def p_error(self, p):
+        # If error recovery is added here in the future, make sure
+        # _get_yacc_lookahead_token still works!
+        #
+        if p:
+            self._parse_error(
+                'before: %s' % p.value,
+                self._coord(lineno=p.lineno,
+                            column=self.clex.find_tok_column(p)))
+        else:
+            self._parse_error('At end of input', self.clex.filename)
diff --git a/.venv/lib/python3.8/site-packages/pycparser/lextab.py b/.venv/lib/python3.8/site-packages/pycparser/lextab.py
new file mode 100644
index 0000000000000000000000000000000000000000..aeb5c152d1ed8f072f308e0a896948796605e033
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/lextab.py
@@ -0,0 +1,10 @@
+# lextab.py. This file automatically created by PLY (version 3.10). Don't edit!
+_tabversion   = '3.10'
+_lextokens    = set(('AND', 'ANDEQUAL', 'ARROW', 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CHAR_CONST', 'COLON', 'COMMA', 'CONDOP', 'CONST', 'CONTINUE', 'DEFAULT', 'DIVEQUAL', 'DIVIDE', 'DO', 'DOUBLE', 'ELLIPSIS', 'ELSE', 'ENUM', 'EQ', 'EQUALS', 'EXTERN', 'FLOAT', 'FLOAT_CONST', 'FOR', 'GE', 'GOTO', 'GT', 'HEX_FLOAT_CONST', 'ID', 'IF', 'INLINE', 'INT', 'INT_CONST_BIN', 'INT_CONST_CHAR', 'INT_CONST_DEC', 'INT_CONST_HEX', 'INT_CONST_OCT', 'LAND', 'LBRACE', 'LBRACKET', 'LE', 'LNOT', 'LONG', 'LOR', 'LPAREN', 'LSHIFT', 'LSHIFTEQUAL', 'LT', 'MINUS', 'MINUSEQUAL', 'MINUSMINUS', 'MOD', 'MODEQUAL', 'NE', 'NOT', 'OFFSETOF', 'OR', 'OREQUAL', 'PERIOD', 'PLUS', 'PLUSEQUAL', 'PLUSPLUS', 'PPHASH', 'PPPRAGMA', 'PPPRAGMASTR', 'RBRACE', 'RBRACKET', 'REGISTER', 'RESTRICT', 'RETURN', 'RPAREN', 'RSHIFT', 'RSHIFTEQUAL', 'SEMI', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRING_LITERAL', 'STRUCT', 'SWITCH', 'TIMES', 'TIMESEQUAL', 'TYPEDEF', 'TYPEID', 'U16CHAR_CONST', 'U16STRING_LITERAL', 'U32CHAR_CONST', 'U32STRING_LITERAL', 'U8CHAR_CONST', 'U8STRING_LITERAL', 'UNION', 'UNSIGNED', 'VOID', 'VOLATILE', 'WCHAR_CONST', 'WHILE', 'WSTRING_LITERAL', 'XOR', 'XOREQUAL', '_ALIGNAS', '_ALIGNOF', '_ATOMIC', '_BOOL', '_COMPLEX', '_NORETURN', '_PRAGMA', '_STATIC_ASSERT', '_THREAD_LOCAL', '__INT128'))
+_lexreflags   = 64
+_lexliterals  = ''
+_lexstateinfo = {'INITIAL': 'inclusive', 'ppline': 'exclusive', 'pppragma': 'exclusive'}
+_lexstatere   = {'INITIAL': [('(?P<t_PPHASH>[ \\t]*\\#)|(?P<t_NEWLINE>\\n+)|(?P<t_LBRACE>\\{)|(?P<t_RBRACE>\\})|(?P<t_FLOAT_CONST>((((([0-9]*\\.[0-9]+)|([0-9]+\\.))([eE][-+]?[0-9]+)?)|([0-9]+([eE][-+]?[0-9]+)))[FfLl]?))|(?P<t_HEX_FLOAT_CONST>(0[xX]([0-9a-fA-F]+|((([0-9a-fA-F]+)?\\.[0-9a-fA-F]+)|([0-9a-fA-F]+\\.)))([pP][+-]?[0-9]+)[FfLl]?))|(?P<t_INT_CONST_HEX>0[xX][0-9a-fA-F]+(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P<t_INT_CONST_BIN>0[bB][01]+(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P<t_BAD_CONST_OCT>0[0-7]*[89])|(?P<t_INT_CONST_OCT>0[0-7]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P<t_INT_CONST_DEC>(0(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|([1-9][0-9]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?))|(?P<t_INT_CONST_CHAR>\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F])))){2,4}\')|(?P<t_CHAR_CONST>\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?P<t_WCHAR_CONST>L\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?P<t_U8CHAR_CONST>u8\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?P<t_U16CHAR_CONST>u\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?P<t_U32CHAR_CONST>U\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?P<t_UNMATCHED_QUOTE>(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))*\\n)|(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))*$))|(?P<t_BAD_CHAR_CONST>(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))[^\'\n]+\')|(\'\')|(\'([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?\'"x0-9])[^\'\\n]*\'))|(?P<t_WSTRING_LITERAL>L"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_U8STRING_LITERAL>u8"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_U16STRING_LITERAL>u"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_U32STRING_LITERAL>U"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_BAD_STRING_LITERAL>"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?\'"x0-9])([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_ID>[a-zA-Z_$][0-9a-zA-Z_$]*)|(?P<t_STRING_LITERAL>"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_ELLIPSIS>\\.\\.\\.)|(?P<t_LOR>\\|\\|)|(?P<t_PLUSPLUS>\\+\\+)|(?P<t_LSHIFTEQUAL><<=)|(?P<t_OREQUAL>\\|=)|(?P<t_PLUSEQUAL>\\+=)|(?P<t_RSHIFTEQUAL>>>=)|(?P<t_TIMESEQUAL>\\*=)|(?P<t_XOREQUAL>\\^=)|(?P<t_ANDEQUAL>&=)|(?P<t_ARROW>->)|(?P<t_CONDOP>\\?)|(?P<t_DIVEQUAL>/=)|(?P<t_EQ>==)|(?P<t_GE>>=)|(?P<t_LAND>&&)|(?P<t_LBRACKET>\\[)|(?P<t_LE><=)|(?P<t_LPAREN>\\()|(?P<t_LSHIFT><<)|(?P<t_MINUSEQUAL>-=)|(?P<t_MINUSMINUS>--)|(?P<t_MODEQUAL>%=)|(?P<t_NE>!=)|(?P<t_OR>\\|)|(?P<t_PERIOD>\\.)|(?P<t_PLUS>\\+)|(?P<t_RBRACKET>\\])|(?P<t_RPAREN>\\))|(?P<t_RSHIFT>>>)|(?P<t_TIMES>\\*)|(?P<t_XOR>\\^)|(?P<t_AND>&)|(?P<t_COLON>:)|(?P<t_COMMA>,)|(?P<t_DIVIDE>/)|(?P<t_EQUALS>=)|(?P<t_GT>>)|(?P<t_LNOT>!)|(?P<t_LT><)|(?P<t_MINUS>-)|(?P<t_MOD>%)|(?P<t_NOT>~)|(?P<t_SEMI>;)', [None, ('t_PPHASH', 'PPHASH'), ('t_NEWLINE', 'NEWLINE'), ('t_LBRACE', 'LBRACE'), ('t_RBRACE', 'RBRACE'), ('t_FLOAT_CONST', 'FLOAT_CONST'), None, None, None, None, None, None, None, None, None, ('t_HEX_FLOAT_CONST', 'HEX_FLOAT_CONST'), None, None, None, None, None, None, None, ('t_INT_CONST_HEX', 'INT_CONST_HEX'), None, None, None, None, None, None, None, ('t_INT_CONST_BIN', 'INT_CONST_BIN'), None, None, None, None, None, None, None, ('t_BAD_CONST_OCT', 'BAD_CONST_OCT'), ('t_INT_CONST_OCT', 'INT_CONST_OCT'), None, None, None, None, None, None, None, ('t_INT_CONST_DEC', 'INT_CONST_DEC'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_INT_CONST_CHAR', 'INT_CONST_CHAR'), None, None, None, None, None, None, ('t_CHAR_CONST', 'CHAR_CONST'), None, None, None, None, None, None, ('t_WCHAR_CONST', 'WCHAR_CONST'), None, None, None, None, None, None, ('t_U8CHAR_CONST', 'U8CHAR_CONST'), None, None, None, None, None, None, ('t_U16CHAR_CONST', 'U16CHAR_CONST'), None, None, None, None, None, None, ('t_U32CHAR_CONST', 'U32CHAR_CONST'), None, None, None, None, None, None, ('t_UNMATCHED_QUOTE', 'UNMATCHED_QUOTE'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_BAD_CHAR_CONST', 'BAD_CHAR_CONST'), None, None, None, None, None, None, None, None, None, None, ('t_WSTRING_LITERAL', 'WSTRING_LITERAL'), None, None, ('t_U8STRING_LITERAL', 'U8STRING_LITERAL'), None, None, ('t_U16STRING_LITERAL', 'U16STRING_LITERAL'), None, None, ('t_U32STRING_LITERAL', 'U32STRING_LITERAL'), None, None, ('t_BAD_STRING_LITERAL', 'BAD_STRING_LITERAL'), None, None, None, None, None, ('t_ID', 'ID'), (None, 'STRING_LITERAL'), None, None, (None, 'ELLIPSIS'), (None, 'LOR'), (None, 'PLUSPLUS'), (None, 'LSHIFTEQUAL'), (None, 'OREQUAL'), (None, 'PLUSEQUAL'), (None, 'RSHIFTEQUAL'), (None, 'TIMESEQUAL'), (None, 'XOREQUAL'), (None, 'ANDEQUAL'), (None, 'ARROW'), (None, 'CONDOP'), (None, 'DIVEQUAL'), (None, 'EQ'), (None, 'GE'), (None, 'LAND'), (None, 'LBRACKET'), (None, 'LE'), (None, 'LPAREN'), (None, 'LSHIFT'), (None, 'MINUSEQUAL'), (None, 'MINUSMINUS'), (None, 'MODEQUAL'), (None, 'NE'), (None, 'OR'), (None, 'PERIOD'), (None, 'PLUS'), (None, 'RBRACKET'), (None, 'RPAREN'), (None, 'RSHIFT'), (None, 'TIMES'), (None, 'XOR'), (None, 'AND'), (None, 'COLON'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'EQUALS'), (None, 'GT'), (None, 'LNOT'), (None, 'LT'), (None, 'MINUS'), (None, 'MOD'), (None, 'NOT'), (None, 'SEMI')])], 'ppline': [('(?P<t_ppline_FILENAME>"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P<t_ppline_LINE_NUMBER>(0(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|([1-9][0-9]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?))|(?P<t_ppline_NEWLINE>\\n)|(?P<t_ppline_PPLINE>line)', [None, ('t_ppline_FILENAME', 'FILENAME'), None, None, ('t_ppline_LINE_NUMBER', 'LINE_NUMBER'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_ppline_NEWLINE', 'NEWLINE'), ('t_ppline_PPLINE', 'PPLINE')])], 'pppragma': [('(?P<t_pppragma_NEWLINE>\\n)|(?P<t_pppragma_PPPRAGMA>pragma)|(?P<t_pppragma_STR>.+)', [None, ('t_pppragma_NEWLINE', 'NEWLINE'), ('t_pppragma_PPPRAGMA', 'PPPRAGMA'), ('t_pppragma_STR', 'STR')])]}
+_lexstateignore = {'INITIAL': ' \t', 'ppline': ' \t', 'pppragma': ' \t'}
+_lexstateerrorf = {'INITIAL': 't_error', 'ppline': 't_ppline_error', 'pppragma': 't_pppragma_error'}
+_lexstateeoff = {}
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ply/__init__.py b/.venv/lib/python3.8/site-packages/pycparser/ply/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e53cddcf6740cfb5317ce75efd7930c19e66f17
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ply/__init__.py
@@ -0,0 +1,5 @@
+# PLY package
+# Author: David Beazley (dave@dabeaz.com)
+
+__version__ = '3.9'
+__all__ = ['lex','yacc']
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ply/cpp.py b/.venv/lib/python3.8/site-packages/pycparser/ply/cpp.py
new file mode 100644
index 0000000000000000000000000000000000000000..86273eac77a5b404cb41cf4d2650d8a37415fb67
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ply/cpp.py
@@ -0,0 +1,905 @@
+# -----------------------------------------------------------------------------
+# cpp.py
+#
+# Author:  David Beazley (http://www.dabeaz.com)
+# Copyright (C) 2017
+# All rights reserved
+#
+# This module implements an ANSI-C style lexical preprocessor for PLY.
+# -----------------------------------------------------------------------------
+import sys
+
+# Some Python 3 compatibility shims
+if sys.version_info.major < 3:
+    STRING_TYPES = (str, unicode)
+else:
+    STRING_TYPES = str
+    xrange = range
+
+# -----------------------------------------------------------------------------
+# Default preprocessor lexer definitions.   These tokens are enough to get
+# a basic preprocessor working.   Other modules may import these if they want
+# -----------------------------------------------------------------------------
+
+tokens = (
+   'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND'
+)
+
+literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\""
+
+# Whitespace
+def t_CPP_WS(t):
+    r'\s+'
+    t.lexer.lineno += t.value.count("\n")
+    return t
+
+t_CPP_POUND = r'\#'
+t_CPP_DPOUND = r'\#\#'
+
+# Identifier
+t_CPP_ID = r'[A-Za-z_][\w_]*'
+
+# Integer literal
+def CPP_INTEGER(t):
+    r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)'
+    return t
+
+t_CPP_INTEGER = CPP_INTEGER
+
+# Floating literal
+t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'
+
+# String literal
+def t_CPP_STRING(t):
+    r'\"([^\\\n]|(\\(.|\n)))*?\"'
+    t.lexer.lineno += t.value.count("\n")
+    return t
+
+# Character constant 'c' or L'c'
+def t_CPP_CHAR(t):
+    r'(L)?\'([^\\\n]|(\\(.|\n)))*?\''
+    t.lexer.lineno += t.value.count("\n")
+    return t
+
+# Comment
+def t_CPP_COMMENT1(t):
+    r'(/\*(.|\n)*?\*/)'
+    ncr = t.value.count("\n")
+    t.lexer.lineno += ncr
+    # replace with one space or a number of '\n'
+    t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' '
+    return t
+
+# Line comment
+def t_CPP_COMMENT2(t):
+    r'(//.*?(\n|$))'
+    # replace with '/n'
+    t.type = 'CPP_WS'; t.value = '\n'
+    return t
+
+def t_error(t):
+    t.type = t.value[0]
+    t.value = t.value[0]
+    t.lexer.skip(1)
+    return t
+
+import re
+import copy
+import time
+import os.path
+
+# -----------------------------------------------------------------------------
+# trigraph()
+#
+# Given an input string, this function replaces all trigraph sequences.
+# The following mapping is used:
+#
+#     ??=    #
+#     ??/    \
+#     ??'    ^
+#     ??(    [
+#     ??)    ]
+#     ??!    |
+#     ??<    {
+#     ??>    }
+#     ??-    ~
+# -----------------------------------------------------------------------------
+
+_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''')
+_trigraph_rep = {
+    '=':'#',
+    '/':'\\',
+    "'":'^',
+    '(':'[',
+    ')':']',
+    '!':'|',
+    '<':'{',
+    '>':'}',
+    '-':'~'
+}
+
+def trigraph(input):
+    return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input)
+
+# ------------------------------------------------------------------
+# Macro object
+#
+# This object holds information about preprocessor macros
+#
+#    .name      - Macro name (string)
+#    .value     - Macro value (a list of tokens)
+#    .arglist   - List of argument names
+#    .variadic  - Boolean indicating whether or not variadic macro
+#    .vararg    - Name of the variadic parameter
+#
+# When a macro is created, the macro replacement token sequence is
+# pre-scanned and used to create patch lists that are later used
+# during macro expansion
+# ------------------------------------------------------------------
+
+class Macro(object):
+    def __init__(self,name,value,arglist=None,variadic=False):
+        self.name = name
+        self.value = value
+        self.arglist = arglist
+        self.variadic = variadic
+        if variadic:
+            self.vararg = arglist[-1]
+        self.source = None
+
+# ------------------------------------------------------------------
+# Preprocessor object
+#
+# Object representing a preprocessor.  Contains macro definitions,
+# include directories, and other information
+# ------------------------------------------------------------------
+
+class Preprocessor(object):
+    def __init__(self,lexer=None):
+        if lexer is None:
+            lexer = lex.lexer
+        self.lexer = lexer
+        self.macros = { }
+        self.path = []
+        self.temp_path = []
+
+        # Probe the lexer for selected tokens
+        self.lexprobe()
+
+        tm = time.localtime()
+        self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm))
+        self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm))
+        self.parser = None
+
+    # -----------------------------------------------------------------------------
+    # tokenize()
+    #
+    # Utility function. Given a string of text, tokenize into a list of tokens
+    # -----------------------------------------------------------------------------
+
+    def tokenize(self,text):
+        tokens = []
+        self.lexer.input(text)
+        while True:
+            tok = self.lexer.token()
+            if not tok: break
+            tokens.append(tok)
+        return tokens
+
+    # ---------------------------------------------------------------------
+    # error()
+    #
+    # Report a preprocessor error/warning of some kind
+    # ----------------------------------------------------------------------
+
+    def error(self,file,line,msg):
+        print("%s:%d %s" % (file,line,msg))
+
+    # ----------------------------------------------------------------------
+    # lexprobe()
+    #
+    # This method probes the preprocessor lexer object to discover
+    # the token types of symbols that are important to the preprocessor.
+    # If this works right, the preprocessor will simply "work"
+    # with any suitable lexer regardless of how tokens have been named.
+    # ----------------------------------------------------------------------
+
+    def lexprobe(self):
+
+        # Determine the token type for identifiers
+        self.lexer.input("identifier")
+        tok = self.lexer.token()
+        if not tok or tok.value != "identifier":
+            print("Couldn't determine identifier type")
+        else:
+            self.t_ID = tok.type
+
+        # Determine the token type for integers
+        self.lexer.input("12345")
+        tok = self.lexer.token()
+        if not tok or int(tok.value) != 12345:
+            print("Couldn't determine integer type")
+        else:
+            self.t_INTEGER = tok.type
+            self.t_INTEGER_TYPE = type(tok.value)
+
+        # Determine the token type for strings enclosed in double quotes
+        self.lexer.input("\"filename\"")
+        tok = self.lexer.token()
+        if not tok or tok.value != "\"filename\"":
+            print("Couldn't determine string type")
+        else:
+            self.t_STRING = tok.type
+
+        # Determine the token type for whitespace--if any
+        self.lexer.input("  ")
+        tok = self.lexer.token()
+        if not tok or tok.value != "  ":
+            self.t_SPACE = None
+        else:
+            self.t_SPACE = tok.type
+
+        # Determine the token type for newlines
+        self.lexer.input("\n")
+        tok = self.lexer.token()
+        if not tok or tok.value != "\n":
+            self.t_NEWLINE = None
+            print("Couldn't determine token for newlines")
+        else:
+            self.t_NEWLINE = tok.type
+
+        self.t_WS = (self.t_SPACE, self.t_NEWLINE)
+
+        # Check for other characters used by the preprocessor
+        chars = [ '<','>','#','##','\\','(',')',',','.']
+        for c in chars:
+            self.lexer.input(c)
+            tok = self.lexer.token()
+            if not tok or tok.value != c:
+                print("Unable to lex '%s' required for preprocessor" % c)
+
+    # ----------------------------------------------------------------------
+    # add_path()
+    #
+    # Adds a search path to the preprocessor.
+    # ----------------------------------------------------------------------
+
+    def add_path(self,path):
+        self.path.append(path)
+
+    # ----------------------------------------------------------------------
+    # group_lines()
+    #
+    # Given an input string, this function splits it into lines.  Trailing whitespace
+    # is removed.   Any line ending with \ is grouped with the next line.  This
+    # function forms the lowest level of the preprocessor---grouping into text into
+    # a line-by-line format.
+    # ----------------------------------------------------------------------
+
+    def group_lines(self,input):
+        lex = self.lexer.clone()
+        lines = [x.rstrip() for x in input.splitlines()]
+        for i in xrange(len(lines)):
+            j = i+1
+            while lines[i].endswith('\\') and (j < len(lines)):
+                lines[i] = lines[i][:-1]+lines[j]
+                lines[j] = ""
+                j += 1
+
+        input = "\n".join(lines)
+        lex.input(input)
+        lex.lineno = 1
+
+        current_line = []
+        while True:
+            tok = lex.token()
+            if not tok:
+                break
+            current_line.append(tok)
+            if tok.type in self.t_WS and '\n' in tok.value:
+                yield current_line
+                current_line = []
+
+        if current_line:
+            yield current_line
+
+    # ----------------------------------------------------------------------
+    # tokenstrip()
+    #
+    # Remove leading/trailing whitespace tokens from a token list
+    # ----------------------------------------------------------------------
+
+    def tokenstrip(self,tokens):
+        i = 0
+        while i < len(tokens) and tokens[i].type in self.t_WS:
+            i += 1
+        del tokens[:i]
+        i = len(tokens)-1
+        while i >= 0 and tokens[i].type in self.t_WS:
+            i -= 1
+        del tokens[i+1:]
+        return tokens
+
+
+    # ----------------------------------------------------------------------
+    # collect_args()
+    #
+    # Collects comma separated arguments from a list of tokens.   The arguments
+    # must be enclosed in parenthesis.  Returns a tuple (tokencount,args,positions)
+    # where tokencount is the number of tokens consumed, args is a list of arguments,
+    # and positions is a list of integers containing the starting index of each
+    # argument.  Each argument is represented by a list of tokens.
+    #
+    # When collecting arguments, leading and trailing whitespace is removed
+    # from each argument.
+    #
+    # This function properly handles nested parenthesis and commas---these do not
+    # define new arguments.
+    # ----------------------------------------------------------------------
+
+    def collect_args(self,tokenlist):
+        args = []
+        positions = []
+        current_arg = []
+        nesting = 1
+        tokenlen = len(tokenlist)
+
+        # Search for the opening '('.
+        i = 0
+        while (i < tokenlen) and (tokenlist[i].type in self.t_WS):
+            i += 1
+
+        if (i < tokenlen) and (tokenlist[i].value == '('):
+            positions.append(i+1)
+        else:
+            self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments")
+            return 0, [], []
+
+        i += 1
+
+        while i < tokenlen:
+            t = tokenlist[i]
+            if t.value == '(':
+                current_arg.append(t)
+                nesting += 1
+            elif t.value == ')':
+                nesting -= 1
+                if nesting == 0:
+                    if current_arg:
+                        args.append(self.tokenstrip(current_arg))
+                        positions.append(i)
+                    return i+1,args,positions
+                current_arg.append(t)
+            elif t.value == ',' and nesting == 1:
+                args.append(self.tokenstrip(current_arg))
+                positions.append(i+1)
+                current_arg = []
+            else:
+                current_arg.append(t)
+            i += 1
+
+        # Missing end argument
+        self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments")
+        return 0, [],[]
+
+    # ----------------------------------------------------------------------
+    # macro_prescan()
+    #
+    # Examine the macro value (token sequence) and identify patch points
+    # This is used to speed up macro expansion later on---we'll know
+    # right away where to apply patches to the value to form the expansion
+    # ----------------------------------------------------------------------
+
+    def macro_prescan(self,macro):
+        macro.patch     = []             # Standard macro arguments
+        macro.str_patch = []             # String conversion expansion
+        macro.var_comma_patch = []       # Variadic macro comma patch
+        i = 0
+        while i < len(macro.value):
+            if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist:
+                argnum = macro.arglist.index(macro.value[i].value)
+                # Conversion of argument to a string
+                if i > 0 and macro.value[i-1].value == '#':
+                    macro.value[i] = copy.copy(macro.value[i])
+                    macro.value[i].type = self.t_STRING
+                    del macro.value[i-1]
+                    macro.str_patch.append((argnum,i-1))
+                    continue
+                # Concatenation
+                elif (i > 0 and macro.value[i-1].value == '##'):
+                    macro.patch.append(('c',argnum,i-1))
+                    del macro.value[i-1]
+                    continue
+                elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'):
+                    macro.patch.append(('c',argnum,i))
+                    i += 1
+                    continue
+                # Standard expansion
+                else:
+                    macro.patch.append(('e',argnum,i))
+            elif macro.value[i].value == '##':
+                if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \
+                        ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \
+                        (macro.value[i+1].value == macro.vararg):
+                    macro.var_comma_patch.append(i-1)
+            i += 1
+        macro.patch.sort(key=lambda x: x[2],reverse=True)
+
+    # ----------------------------------------------------------------------
+    # macro_expand_args()
+    #
+    # Given a Macro and list of arguments (each a token list), this method
+    # returns an expanded version of a macro.  The return value is a token sequence
+    # representing the replacement macro tokens
+    # ----------------------------------------------------------------------
+
+    def macro_expand_args(self,macro,args):
+        # Make a copy of the macro token sequence
+        rep = [copy.copy(_x) for _x in macro.value]
+
+        # Make string expansion patches.  These do not alter the length of the replacement sequence
+
+        str_expansion = {}
+        for argnum, i in macro.str_patch:
+            if argnum not in str_expansion:
+                str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\")
+            rep[i] = copy.copy(rep[i])
+            rep[i].value = str_expansion[argnum]
+
+        # Make the variadic macro comma patch.  If the variadic macro argument is empty, we get rid
+        comma_patch = False
+        if macro.variadic and not args[-1]:
+            for i in macro.var_comma_patch:
+                rep[i] = None
+                comma_patch = True
+
+        # Make all other patches.   The order of these matters.  It is assumed that the patch list
+        # has been sorted in reverse order of patch location since replacements will cause the
+        # size of the replacement sequence to expand from the patch point.
+
+        expanded = { }
+        for ptype, argnum, i in macro.patch:
+            # Concatenation.   Argument is left unexpanded
+            if ptype == 'c':
+                rep[i:i+1] = args[argnum]
+            # Normal expansion.  Argument is macro expanded first
+            elif ptype == 'e':
+                if argnum not in expanded:
+                    expanded[argnum] = self.expand_macros(args[argnum])
+                rep[i:i+1] = expanded[argnum]
+
+        # Get rid of removed comma if necessary
+        if comma_patch:
+            rep = [_i for _i in rep if _i]
+
+        return rep
+
+
+    # ----------------------------------------------------------------------
+    # expand_macros()
+    #
+    # Given a list of tokens, this function performs macro expansion.
+    # The expanded argument is a dictionary that contains macros already
+    # expanded.  This is used to prevent infinite recursion.
+    # ----------------------------------------------------------------------
+
+    def expand_macros(self,tokens,expanded=None):
+        if expanded is None:
+            expanded = {}
+        i = 0
+        while i < len(tokens):
+            t = tokens[i]
+            if t.type == self.t_ID:
+                if t.value in self.macros and t.value not in expanded:
+                    # Yes, we found a macro match
+                    expanded[t.value] = True
+
+                    m = self.macros[t.value]
+                    if not m.arglist:
+                        # A simple macro
+                        ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded)
+                        for e in ex:
+                            e.lineno = t.lineno
+                        tokens[i:i+1] = ex
+                        i += len(ex)
+                    else:
+                        # A macro with arguments
+                        j = i + 1
+                        while j < len(tokens) and tokens[j].type in self.t_WS:
+                            j += 1
+                        if tokens[j].value == '(':
+                            tokcount,args,positions = self.collect_args(tokens[j:])
+                            if not m.variadic and len(args) !=  len(m.arglist):
+                                self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist)))
+                                i = j + tokcount
+                            elif m.variadic and len(args) < len(m.arglist)-1:
+                                if len(m.arglist) > 2:
+                                    self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1))
+                                else:
+                                    self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1))
+                                i = j + tokcount
+                            else:
+                                if m.variadic:
+                                    if len(args) == len(m.arglist)-1:
+                                        args.append([])
+                                    else:
+                                        args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1]
+                                        del args[len(m.arglist):]
+
+                                # Get macro replacement text
+                                rep = self.macro_expand_args(m,args)
+                                rep = self.expand_macros(rep,expanded)
+                                for r in rep:
+                                    r.lineno = t.lineno
+                                tokens[i:j+tokcount] = rep
+                                i += len(rep)
+                    del expanded[t.value]
+                    continue
+                elif t.value == '__LINE__':
+                    t.type = self.t_INTEGER
+                    t.value = self.t_INTEGER_TYPE(t.lineno)
+
+            i += 1
+        return tokens
+
+    # ----------------------------------------------------------------------
+    # evalexpr()
+    #
+    # Evaluate an expression token sequence for the purposes of evaluating
+    # integral expressions.
+    # ----------------------------------------------------------------------
+
+    def evalexpr(self,tokens):
+        # tokens = tokenize(line)
+        # Search for defined macros
+        i = 0
+        while i < len(tokens):
+            if tokens[i].type == self.t_ID and tokens[i].value == 'defined':
+                j = i + 1
+                needparen = False
+                result = "0L"
+                while j < len(tokens):
+                    if tokens[j].type in self.t_WS:
+                        j += 1
+                        continue
+                    elif tokens[j].type == self.t_ID:
+                        if tokens[j].value in self.macros:
+                            result = "1L"
+                        else:
+                            result = "0L"
+                        if not needparen: break
+                    elif tokens[j].value == '(':
+                        needparen = True
+                    elif tokens[j].value == ')':
+                        break
+                    else:
+                        self.error(self.source,tokens[i].lineno,"Malformed defined()")
+                    j += 1
+                tokens[i].type = self.t_INTEGER
+                tokens[i].value = self.t_INTEGER_TYPE(result)
+                del tokens[i+1:j+1]
+            i += 1
+        tokens = self.expand_macros(tokens)
+        for i,t in enumerate(tokens):
+            if t.type == self.t_ID:
+                tokens[i] = copy.copy(t)
+                tokens[i].type = self.t_INTEGER
+                tokens[i].value = self.t_INTEGER_TYPE("0L")
+            elif t.type == self.t_INTEGER:
+                tokens[i] = copy.copy(t)
+                # Strip off any trailing suffixes
+                tokens[i].value = str(tokens[i].value)
+                while tokens[i].value[-1] not in "0123456789abcdefABCDEF":
+                    tokens[i].value = tokens[i].value[:-1]
+
+        expr = "".join([str(x.value) for x in tokens])
+        expr = expr.replace("&&"," and ")
+        expr = expr.replace("||"," or ")
+        expr = expr.replace("!"," not ")
+        try:
+            result = eval(expr)
+        except Exception:
+            self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression")
+            result = 0
+        return result
+
+    # ----------------------------------------------------------------------
+    # parsegen()
+    #
+    # Parse an input string/
+    # ----------------------------------------------------------------------
+    def parsegen(self,input,source=None):
+
+        # Replace trigraph sequences
+        t = trigraph(input)
+        lines = self.group_lines(t)
+
+        if not source:
+            source = ""
+
+        self.define("__FILE__ \"%s\"" % source)
+
+        self.source = source
+        chunk = []
+        enable = True
+        iftrigger = False
+        ifstack = []
+
+        for x in lines:
+            for i,tok in enumerate(x):
+                if tok.type not in self.t_WS: break
+            if tok.value == '#':
+                # Preprocessor directive
+
+                # insert necessary whitespace instead of eaten tokens
+                for tok in x:
+                    if tok.type in self.t_WS and '\n' in tok.value:
+                        chunk.append(tok)
+
+                dirtokens = self.tokenstrip(x[i+1:])
+                if dirtokens:
+                    name = dirtokens[0].value
+                    args = self.tokenstrip(dirtokens[1:])
+                else:
+                    name = ""
+                    args = []
+
+                if name == 'define':
+                    if enable:
+                        for tok in self.expand_macros(chunk):
+                            yield tok
+                        chunk = []
+                        self.define(args)
+                elif name == 'include':
+                    if enable:
+                        for tok in self.expand_macros(chunk):
+                            yield tok
+                        chunk = []
+                        oldfile = self.macros['__FILE__']
+                        for tok in self.include(args):
+                            yield tok
+                        self.macros['__FILE__'] = oldfile
+                        self.source = source
+                elif name == 'undef':
+                    if enable:
+                        for tok in self.expand_macros(chunk):
+                            yield tok
+                        chunk = []
+                        self.undef(args)
+                elif name == 'ifdef':
+                    ifstack.append((enable,iftrigger))
+                    if enable:
+                        if not args[0].value in self.macros:
+                            enable = False
+                            iftrigger = False
+                        else:
+                            iftrigger = True
+                elif name == 'ifndef':
+                    ifstack.append((enable,iftrigger))
+                    if enable:
+                        if args[0].value in self.macros:
+                            enable = False
+                            iftrigger = False
+                        else:
+                            iftrigger = True
+                elif name == 'if':
+                    ifstack.append((enable,iftrigger))
+                    if enable:
+                        result = self.evalexpr(args)
+                        if not result:
+                            enable = False
+                            iftrigger = False
+                        else:
+                            iftrigger = True
+                elif name == 'elif':
+                    if ifstack:
+                        if ifstack[-1][0]:     # We only pay attention if outer "if" allows this
+                            if enable:         # If already true, we flip enable False
+                                enable = False
+                            elif not iftrigger:   # If False, but not triggered yet, we'll check expression
+                                result = self.evalexpr(args)
+                                if result:
+                                    enable  = True
+                                    iftrigger = True
+                    else:
+                        self.error(self.source,dirtokens[0].lineno,"Misplaced #elif")
+
+                elif name == 'else':
+                    if ifstack:
+                        if ifstack[-1][0]:
+                            if enable:
+                                enable = False
+                            elif not iftrigger:
+                                enable = True
+                                iftrigger = True
+                    else:
+                        self.error(self.source,dirtokens[0].lineno,"Misplaced #else")
+
+                elif name == 'endif':
+                    if ifstack:
+                        enable,iftrigger = ifstack.pop()
+                    else:
+                        self.error(self.source,dirtokens[0].lineno,"Misplaced #endif")
+                else:
+                    # Unknown preprocessor directive
+                    pass
+
+            else:
+                # Normal text
+                if enable:
+                    chunk.extend(x)
+
+        for tok in self.expand_macros(chunk):
+            yield tok
+        chunk = []
+
+    # ----------------------------------------------------------------------
+    # include()
+    #
+    # Implementation of file-inclusion
+    # ----------------------------------------------------------------------
+
+    def include(self,tokens):
+        # Try to extract the filename and then process an include file
+        if not tokens:
+            return
+        if tokens:
+            if tokens[0].value != '<' and tokens[0].type != self.t_STRING:
+                tokens = self.expand_macros(tokens)
+
+            if tokens[0].value == '<':
+                # Include <...>
+                i = 1
+                while i < len(tokens):
+                    if tokens[i].value == '>':
+                        break
+                    i += 1
+                else:
+                    print("Malformed #include <...>")
+                    return
+                filename = "".join([x.value for x in tokens[1:i]])
+                path = self.path + [""] + self.temp_path
+            elif tokens[0].type == self.t_STRING:
+                filename = tokens[0].value[1:-1]
+                path = self.temp_path + [""] + self.path
+            else:
+                print("Malformed #include statement")
+                return
+        for p in path:
+            iname = os.path.join(p,filename)
+            try:
+                data = open(iname,"r").read()
+                dname = os.path.dirname(iname)
+                if dname:
+                    self.temp_path.insert(0,dname)
+                for tok in self.parsegen(data,filename):
+                    yield tok
+                if dname:
+                    del self.temp_path[0]
+                break
+            except IOError:
+                pass
+        else:
+            print("Couldn't find '%s'" % filename)
+
+    # ----------------------------------------------------------------------
+    # define()
+    #
+    # Define a new macro
+    # ----------------------------------------------------------------------
+
+    def define(self,tokens):
+        if isinstance(tokens,STRING_TYPES):
+            tokens = self.tokenize(tokens)
+
+        linetok = tokens
+        try:
+            name = linetok[0]
+            if len(linetok) > 1:
+                mtype = linetok[1]
+            else:
+                mtype = None
+            if not mtype:
+                m = Macro(name.value,[])
+                self.macros[name.value] = m
+            elif mtype.type in self.t_WS:
+                # A normal macro
+                m = Macro(name.value,self.tokenstrip(linetok[2:]))
+                self.macros[name.value] = m
+            elif mtype.value == '(':
+                # A macro with arguments
+                tokcount, args, positions = self.collect_args(linetok[1:])
+                variadic = False
+                for a in args:
+                    if variadic:
+                        print("No more arguments may follow a variadic argument")
+                        break
+                    astr = "".join([str(_i.value) for _i in a])
+                    if astr == "...":
+                        variadic = True
+                        a[0].type = self.t_ID
+                        a[0].value = '__VA_ARGS__'
+                        variadic = True
+                        del a[1:]
+                        continue
+                    elif astr[-3:] == "..." and a[0].type == self.t_ID:
+                        variadic = True
+                        del a[1:]
+                        # If, for some reason, "." is part of the identifier, strip off the name for the purposes
+                        # of macro expansion
+                        if a[0].value[-3:] == '...':
+                            a[0].value = a[0].value[:-3]
+                        continue
+                    if len(a) > 1 or a[0].type != self.t_ID:
+                        print("Invalid macro argument")
+                        break
+                else:
+                    mvalue = self.tokenstrip(linetok[1+tokcount:])
+                    i = 0
+                    while i < len(mvalue):
+                        if i+1 < len(mvalue):
+                            if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##':
+                                del mvalue[i]
+                                continue
+                            elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS:
+                                del mvalue[i+1]
+                        i += 1
+                    m = Macro(name.value,mvalue,[x[0].value for x in args],variadic)
+                    self.macro_prescan(m)
+                    self.macros[name.value] = m
+            else:
+                print("Bad macro definition")
+        except LookupError:
+            print("Bad macro definition")
+
+    # ----------------------------------------------------------------------
+    # undef()
+    #
+    # Undefine a macro
+    # ----------------------------------------------------------------------
+
+    def undef(self,tokens):
+        id = tokens[0].value
+        try:
+            del self.macros[id]
+        except LookupError:
+            pass
+
+    # ----------------------------------------------------------------------
+    # parse()
+    #
+    # Parse input text.
+    # ----------------------------------------------------------------------
+    def parse(self,input,source=None,ignore={}):
+        self.ignore = ignore
+        self.parser = self.parsegen(input,source)
+
+    # ----------------------------------------------------------------------
+    # token()
+    #
+    # Method to return individual tokens
+    # ----------------------------------------------------------------------
+    def token(self):
+        try:
+            while True:
+                tok = next(self.parser)
+                if tok.type not in self.ignore: return tok
+        except StopIteration:
+            self.parser = None
+            return None
+
+if __name__ == '__main__':
+    import ply.lex as lex
+    lexer = lex.lex()
+
+    # Run a preprocessor
+    import sys
+    f = open(sys.argv[1])
+    input = f.read()
+
+    p = Preprocessor(lexer)
+    p.parse(input,sys.argv[1])
+    while True:
+        tok = p.token()
+        if not tok: break
+        print(p.source, tok)
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ply/ctokens.py b/.venv/lib/python3.8/site-packages/pycparser/ply/ctokens.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6f6952d605ee5fa0a25eff03f18769b6b445fae
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ply/ctokens.py
@@ -0,0 +1,133 @@
+# ----------------------------------------------------------------------
+# ctokens.py
+#
+# Token specifications for symbols in ANSI C and C++.  This file is
+# meant to be used as a library in other tokenizers.
+# ----------------------------------------------------------------------
+
+# Reserved words
+
+tokens = [
+    # Literals (identifier, integer constant, float constant, string constant, char const)
+    'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER',
+
+    # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=)
+    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO',
+    'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
+    'LOR', 'LAND', 'LNOT',
+    'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
+    
+    # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=)
+    'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL',
+    'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',
+
+    # Increment/decrement (++,--)
+    'INCREMENT', 'DECREMENT',
+
+    # Structure dereference (->)
+    'ARROW',
+
+    # Ternary operator (?)
+    'TERNARY',
+    
+    # Delimeters ( ) [ ] { } , . ; :
+    'LPAREN', 'RPAREN',
+    'LBRACKET', 'RBRACKET',
+    'LBRACE', 'RBRACE',
+    'COMMA', 'PERIOD', 'SEMI', 'COLON',
+
+    # Ellipsis (...)
+    'ELLIPSIS',
+]
+    
+# Operators
+t_PLUS             = r'\+'
+t_MINUS            = r'-'
+t_TIMES            = r'\*'
+t_DIVIDE           = r'/'
+t_MODULO           = r'%'
+t_OR               = r'\|'
+t_AND              = r'&'
+t_NOT              = r'~'
+t_XOR              = r'\^'
+t_LSHIFT           = r'<<'
+t_RSHIFT           = r'>>'
+t_LOR              = r'\|\|'
+t_LAND             = r'&&'
+t_LNOT             = r'!'
+t_LT               = r'<'
+t_GT               = r'>'
+t_LE               = r'<='
+t_GE               = r'>='
+t_EQ               = r'=='
+t_NE               = r'!='
+
+# Assignment operators
+
+t_EQUALS           = r'='
+t_TIMESEQUAL       = r'\*='
+t_DIVEQUAL         = r'/='
+t_MODEQUAL         = r'%='
+t_PLUSEQUAL        = r'\+='
+t_MINUSEQUAL       = r'-='
+t_LSHIFTEQUAL      = r'<<='
+t_RSHIFTEQUAL      = r'>>='
+t_ANDEQUAL         = r'&='
+t_OREQUAL          = r'\|='
+t_XOREQUAL         = r'\^='
+
+# Increment/decrement
+t_INCREMENT        = r'\+\+'
+t_DECREMENT        = r'--'
+
+# ->
+t_ARROW            = r'->'
+
+# ?
+t_TERNARY          = r'\?'
+
+# Delimeters
+t_LPAREN           = r'\('
+t_RPAREN           = r'\)'
+t_LBRACKET         = r'\['
+t_RBRACKET         = r'\]'
+t_LBRACE           = r'\{'
+t_RBRACE           = r'\}'
+t_COMMA            = r','
+t_PERIOD           = r'\.'
+t_SEMI             = r';'
+t_COLON            = r':'
+t_ELLIPSIS         = r'\.\.\.'
+
+# Identifiers
+t_ID = r'[A-Za-z_][A-Za-z0-9_]*'
+
+# Integer literal
+t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?'
+
+# Floating literal
+t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'
+
+# String literal
+t_STRING = r'\"([^\\\n]|(\\.))*?\"'
+
+# Character constant 'c' or L'c'
+t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\''
+
+# Comment (C-Style)
+def t_COMMENT(t):
+    r'/\*(.|\n)*?\*/'
+    t.lexer.lineno += t.value.count('\n')
+    return t
+
+# Comment (C++-Style)
+def t_CPPCOMMENT(t):
+    r'//.*\n'
+    t.lexer.lineno += 1
+    return t
+
+
+    
+
+
+
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ply/lex.py b/.venv/lib/python3.8/site-packages/pycparser/ply/lex.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfc5139486c1f675e86b968646e5f756547c2f46
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ply/lex.py
@@ -0,0 +1,1099 @@
+# -----------------------------------------------------------------------------
+# ply: lex.py
+#
+# Copyright (C) 2001-2017
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+#   endorse or promote products derived from this software without
+#  specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+
+__version__    = '3.10'
+__tabversion__ = '3.10'
+
+import re
+import sys
+import types
+import copy
+import os
+import inspect
+
+# This tuple contains known string types
+try:
+    # Python 2.6
+    StringTypes = (types.StringType, types.UnicodeType)
+except AttributeError:
+    # Python 3.0
+    StringTypes = (str, bytes)
+
+# This regular expression is used to match valid token names
+_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
+
+# Exception thrown when invalid token encountered and no default error
+# handler is defined.
+class LexError(Exception):
+    def __init__(self, message, s):
+        self.args = (message,)
+        self.text = s
+
+
+# Token class.  This class is used to represent the tokens produced.
+class LexToken(object):
+    def __str__(self):
+        return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos)
+
+    def __repr__(self):
+        return str(self)
+
+
+# This object is a stand-in for a logging object created by the
+# logging module.
+
+class PlyLogger(object):
+    def __init__(self, f):
+        self.f = f
+
+    def critical(self, msg, *args, **kwargs):
+        self.f.write((msg % args) + '\n')
+
+    def warning(self, msg, *args, **kwargs):
+        self.f.write('WARNING: ' + (msg % args) + '\n')
+
+    def error(self, msg, *args, **kwargs):
+        self.f.write('ERROR: ' + (msg % args) + '\n')
+
+    info = critical
+    debug = critical
+
+
+# Null logger is used when no output is generated. Does nothing.
+class NullLogger(object):
+    def __getattribute__(self, name):
+        return self
+
+    def __call__(self, *args, **kwargs):
+        return self
+
+
+# -----------------------------------------------------------------------------
+#                        === Lexing Engine ===
+#
+# The following Lexer class implements the lexer runtime.   There are only
+# a few public methods and attributes:
+#
+#    input()          -  Store a new string in the lexer
+#    token()          -  Get the next token
+#    clone()          -  Clone the lexer
+#
+#    lineno           -  Current line number
+#    lexpos           -  Current position in the input string
+# -----------------------------------------------------------------------------
+
+class Lexer:
+    def __init__(self):
+        self.lexre = None             # Master regular expression. This is a list of
+                                      # tuples (re, findex) where re is a compiled
+                                      # regular expression and findex is a list
+                                      # mapping regex group numbers to rules
+        self.lexretext = None         # Current regular expression strings
+        self.lexstatere = {}          # Dictionary mapping lexer states to master regexs
+        self.lexstateretext = {}      # Dictionary mapping lexer states to regex strings
+        self.lexstaterenames = {}     # Dictionary mapping lexer states to symbol names
+        self.lexstate = 'INITIAL'     # Current lexer state
+        self.lexstatestack = []       # Stack of lexer states
+        self.lexstateinfo = None      # State information
+        self.lexstateignore = {}      # Dictionary of ignored characters for each state
+        self.lexstateerrorf = {}      # Dictionary of error functions for each state
+        self.lexstateeoff = {}        # Dictionary of eof functions for each state
+        self.lexreflags = 0           # Optional re compile flags
+        self.lexdata = None           # Actual input data (as a string)
+        self.lexpos = 0               # Current position in input text
+        self.lexlen = 0               # Length of the input text
+        self.lexerrorf = None         # Error rule (if any)
+        self.lexeoff = None           # EOF rule (if any)
+        self.lextokens = None         # List of valid tokens
+        self.lexignore = ''           # Ignored characters
+        self.lexliterals = ''         # Literal characters that can be passed through
+        self.lexmodule = None         # Module
+        self.lineno = 1               # Current line number
+        self.lexoptimize = False      # Optimized mode
+
+    def clone(self, object=None):
+        c = copy.copy(self)
+
+        # If the object parameter has been supplied, it means we are attaching the
+        # lexer to a new object.  In this case, we have to rebind all methods in
+        # the lexstatere and lexstateerrorf tables.
+
+        if object:
+            newtab = {}
+            for key, ritem in self.lexstatere.items():
+                newre = []
+                for cre, findex in ritem:
+                    newfindex = []
+                    for f in findex:
+                        if not f or not f[0]:
+                            newfindex.append(f)
+                            continue
+                        newfindex.append((getattr(object, f[0].__name__), f[1]))
+                newre.append((cre, newfindex))
+                newtab[key] = newre
+            c.lexstatere = newtab
+            c.lexstateerrorf = {}
+            for key, ef in self.lexstateerrorf.items():
+                c.lexstateerrorf[key] = getattr(object, ef.__name__)
+            c.lexmodule = object
+        return c
+
+    # ------------------------------------------------------------
+    # writetab() - Write lexer information to a table file
+    # ------------------------------------------------------------
+    def writetab(self, lextab, outputdir=''):
+        if isinstance(lextab, types.ModuleType):
+            raise IOError("Won't overwrite existing lextab module")
+        basetabmodule = lextab.split('.')[-1]
+        filename = os.path.join(outputdir, basetabmodule) + '.py'
+        with open(filename, 'w') as tf:
+            tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__))
+            tf.write('_tabversion   = %s\n' % repr(__tabversion__))
+            tf.write('_lextokens    = set(%s)\n' % repr(tuple(sorted(self.lextokens))))
+            tf.write('_lexreflags   = %s\n' % repr(self.lexreflags))
+            tf.write('_lexliterals  = %s\n' % repr(self.lexliterals))
+            tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo))
+
+            # Rewrite the lexstatere table, replacing function objects with function names
+            tabre = {}
+            for statename, lre in self.lexstatere.items():
+                titem = []
+                for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]):
+                    titem.append((retext, _funcs_to_names(func, renames)))
+                tabre[statename] = titem
+
+            tf.write('_lexstatere   = %s\n' % repr(tabre))
+            tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore))
+
+            taberr = {}
+            for statename, ef in self.lexstateerrorf.items():
+                taberr[statename] = ef.__name__ if ef else None
+            tf.write('_lexstateerrorf = %s\n' % repr(taberr))
+
+            tabeof = {}
+            for statename, ef in self.lexstateeoff.items():
+                tabeof[statename] = ef.__name__ if ef else None
+            tf.write('_lexstateeoff = %s\n' % repr(tabeof))
+
+    # ------------------------------------------------------------
+    # readtab() - Read lexer information from a tab file
+    # ------------------------------------------------------------
+    def readtab(self, tabfile, fdict):
+        if isinstance(tabfile, types.ModuleType):
+            lextab = tabfile
+        else:
+            exec('import %s' % tabfile)
+            lextab = sys.modules[tabfile]
+
+        if getattr(lextab, '_tabversion', '0.0') != __tabversion__:
+            raise ImportError('Inconsistent PLY version')
+
+        self.lextokens      = lextab._lextokens
+        self.lexreflags     = lextab._lexreflags
+        self.lexliterals    = lextab._lexliterals
+        self.lextokens_all  = self.lextokens | set(self.lexliterals)
+        self.lexstateinfo   = lextab._lexstateinfo
+        self.lexstateignore = lextab._lexstateignore
+        self.lexstatere     = {}
+        self.lexstateretext = {}
+        for statename, lre in lextab._lexstatere.items():
+            titem = []
+            txtitem = []
+            for pat, func_name in lre:
+                titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict)))
+
+            self.lexstatere[statename] = titem
+            self.lexstateretext[statename] = txtitem
+
+        self.lexstateerrorf = {}
+        for statename, ef in lextab._lexstateerrorf.items():
+            self.lexstateerrorf[statename] = fdict[ef]
+
+        self.lexstateeoff = {}
+        for statename, ef in lextab._lexstateeoff.items():
+            self.lexstateeoff[statename] = fdict[ef]
+
+        self.begin('INITIAL')
+
+    # ------------------------------------------------------------
+    # input() - Push a new string into the lexer
+    # ------------------------------------------------------------
+    def input(self, s):
+        # Pull off the first character to see if s looks like a string
+        c = s[:1]
+        if not isinstance(c, StringTypes):
+            raise ValueError('Expected a string')
+        self.lexdata = s
+        self.lexpos = 0
+        self.lexlen = len(s)
+
+    # ------------------------------------------------------------
+    # begin() - Changes the lexing state
+    # ------------------------------------------------------------
+    def begin(self, state):
+        if state not in self.lexstatere:
+            raise ValueError('Undefined state')
+        self.lexre = self.lexstatere[state]
+        self.lexretext = self.lexstateretext[state]
+        self.lexignore = self.lexstateignore.get(state, '')
+        self.lexerrorf = self.lexstateerrorf.get(state, None)
+        self.lexeoff = self.lexstateeoff.get(state, None)
+        self.lexstate = state
+
+    # ------------------------------------------------------------
+    # push_state() - Changes the lexing state and saves old on stack
+    # ------------------------------------------------------------
+    def push_state(self, state):
+        self.lexstatestack.append(self.lexstate)
+        self.begin(state)
+
+    # ------------------------------------------------------------
+    # pop_state() - Restores the previous state
+    # ------------------------------------------------------------
+    def pop_state(self):
+        self.begin(self.lexstatestack.pop())
+
+    # ------------------------------------------------------------
+    # current_state() - Returns the current lexing state
+    # ------------------------------------------------------------
+    def current_state(self):
+        return self.lexstate
+
+    # ------------------------------------------------------------
+    # skip() - Skip ahead n characters
+    # ------------------------------------------------------------
+    def skip(self, n):
+        self.lexpos += n
+
+    # ------------------------------------------------------------
+    # opttoken() - Return the next token from the Lexer
+    #
+    # Note: This function has been carefully implemented to be as fast
+    # as possible.  Don't make changes unless you really know what
+    # you are doing
+    # ------------------------------------------------------------
+    def token(self):
+        # Make local copies of frequently referenced attributes
+        lexpos    = self.lexpos
+        lexlen    = self.lexlen
+        lexignore = self.lexignore
+        lexdata   = self.lexdata
+
+        while lexpos < lexlen:
+            # This code provides some short-circuit code for whitespace, tabs, and other ignored characters
+            if lexdata[lexpos] in lexignore:
+                lexpos += 1
+                continue
+
+            # Look for a regular expression match
+            for lexre, lexindexfunc in self.lexre:
+                m = lexre.match(lexdata, lexpos)
+                if not m:
+                    continue
+
+                # Create a token for return
+                tok = LexToken()
+                tok.value = m.group()
+                tok.lineno = self.lineno
+                tok.lexpos = lexpos
+
+                i = m.lastindex
+                func, tok.type = lexindexfunc[i]
+
+                if not func:
+                    # If no token type was set, it's an ignored token
+                    if tok.type:
+                        self.lexpos = m.end()
+                        return tok
+                    else:
+                        lexpos = m.end()
+                        break
+
+                lexpos = m.end()
+
+                # If token is processed by a function, call it
+
+                tok.lexer = self      # Set additional attributes useful in token rules
+                self.lexmatch = m
+                self.lexpos = lexpos
+
+                newtok = func(tok)
+
+                # Every function must return a token, if nothing, we just move to next token
+                if not newtok:
+                    lexpos    = self.lexpos         # This is here in case user has updated lexpos.
+                    lexignore = self.lexignore      # This is here in case there was a state change
+                    break
+
+                # Verify type of the token.  If not in the token map, raise an error
+                if not self.lexoptimize:
+                    if newtok.type not in self.lextokens_all:
+                        raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
+                            func.__code__.co_filename, func.__code__.co_firstlineno,
+                            func.__name__, newtok.type), lexdata[lexpos:])
+
+                return newtok
+            else:
+                # No match, see if in literals
+                if lexdata[lexpos] in self.lexliterals:
+                    tok = LexToken()
+                    tok.value = lexdata[lexpos]
+                    tok.lineno = self.lineno
+                    tok.type = tok.value
+                    tok.lexpos = lexpos
+                    self.lexpos = lexpos + 1
+                    return tok
+
+                # No match. Call t_error() if defined.
+                if self.lexerrorf:
+                    tok = LexToken()
+                    tok.value = self.lexdata[lexpos:]
+                    tok.lineno = self.lineno
+                    tok.type = 'error'
+                    tok.lexer = self
+                    tok.lexpos = lexpos
+                    self.lexpos = lexpos
+                    newtok = self.lexerrorf(tok)
+                    if lexpos == self.lexpos:
+                        # Error method didn't change text position at all. This is an error.
+                        raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
+                    lexpos = self.lexpos
+                    if not newtok:
+                        continue
+                    return newtok
+
+                self.lexpos = lexpos
+                raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:])
+
+        if self.lexeoff:
+            tok = LexToken()
+            tok.type = 'eof'
+            tok.value = ''
+            tok.lineno = self.lineno
+            tok.lexpos = lexpos
+            tok.lexer = self
+            self.lexpos = lexpos
+            newtok = self.lexeoff(tok)
+            return newtok
+
+        self.lexpos = lexpos + 1
+        if self.lexdata is None:
+            raise RuntimeError('No input string given with input()')
+        return None
+
+    # Iterator interface
+    def __iter__(self):
+        return self
+
+    def next(self):
+        t = self.token()
+        if t is None:
+            raise StopIteration
+        return t
+
+    __next__ = next
+
+# -----------------------------------------------------------------------------
+#                           ==== Lex Builder ===
+#
+# The functions and classes below are used to collect lexing information
+# and build a Lexer object from it.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# _get_regex(func)
+#
+# Returns the regular expression assigned to a function either as a doc string
+# or as a .regex attribute attached by the @TOKEN decorator.
+# -----------------------------------------------------------------------------
+def _get_regex(func):
+    return getattr(func, 'regex', func.__doc__)
+
+# -----------------------------------------------------------------------------
+# get_caller_module_dict()
+#
+# This function returns a dictionary containing all of the symbols defined within
+# a caller further down the call stack.  This is used to get the environment
+# associated with the yacc() call if none was provided.
+# -----------------------------------------------------------------------------
+def get_caller_module_dict(levels):
+    f = sys._getframe(levels)
+    ldict = f.f_globals.copy()
+    if f.f_globals != f.f_locals:
+        ldict.update(f.f_locals)
+    return ldict
+
+# -----------------------------------------------------------------------------
+# _funcs_to_names()
+#
+# Given a list of regular expression functions, this converts it to a list
+# suitable for output to a table file
+# -----------------------------------------------------------------------------
+def _funcs_to_names(funclist, namelist):
+    result = []
+    for f, name in zip(funclist, namelist):
+        if f and f[0]:
+            result.append((name, f[1]))
+        else:
+            result.append(f)
+    return result
+
+# -----------------------------------------------------------------------------
+# _names_to_funcs()
+#
+# Given a list of regular expression function names, this converts it back to
+# functions.
+# -----------------------------------------------------------------------------
+def _names_to_funcs(namelist, fdict):
+    result = []
+    for n in namelist:
+        if n and n[0]:
+            result.append((fdict[n[0]], n[1]))
+        else:
+            result.append(n)
+    return result
+
+# -----------------------------------------------------------------------------
+# _form_master_re()
+#
+# This function takes a list of all of the regex components and attempts to
+# form the master regular expression.  Given limitations in the Python re
+# module, it may be necessary to break the master regex into separate expressions.
+# -----------------------------------------------------------------------------
+def _form_master_re(relist, reflags, ldict, toknames):
+    if not relist:
+        return []
+    regex = '|'.join(relist)
+    try:
+        lexre = re.compile(regex, reflags)
+
+        # Build the index to function map for the matching engine
+        lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1)
+        lexindexnames = lexindexfunc[:]
+
+        for f, i in lexre.groupindex.items():
+            handle = ldict.get(f, None)
+            if type(handle) in (types.FunctionType, types.MethodType):
+                lexindexfunc[i] = (handle, toknames[f])
+                lexindexnames[i] = f
+            elif handle is not None:
+                lexindexnames[i] = f
+                if f.find('ignore_') > 0:
+                    lexindexfunc[i] = (None, None)
+                else:
+                    lexindexfunc[i] = (None, toknames[f])
+
+        return [(lexre, lexindexfunc)], [regex], [lexindexnames]
+    except Exception:
+        m = int(len(relist)/2)
+        if m == 0:
+            m = 1
+        llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames)
+        rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames)
+        return (llist+rlist), (lre+rre), (lnames+rnames)
+
+# -----------------------------------------------------------------------------
+# def _statetoken(s,names)
+#
+# Given a declaration name s of the form "t_" and a dictionary whose keys are
+# state names, this function returns a tuple (states,tokenname) where states
+# is a tuple of state names and tokenname is the name of the token.  For example,
+# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
+# -----------------------------------------------------------------------------
+def _statetoken(s, names):
+    nonstate = 1
+    parts = s.split('_')
+    for i, part in enumerate(parts[1:], 1):
+        if part not in names and part != 'ANY':
+            break
+
+    if i > 1:
+        states = tuple(parts[1:i])
+    else:
+        states = ('INITIAL',)
+
+    if 'ANY' in states:
+        states = tuple(names)
+
+    tokenname = '_'.join(parts[i:])
+    return (states, tokenname)
+
+
+# -----------------------------------------------------------------------------
+# LexerReflect()
+#
+# This class represents information needed to build a lexer as extracted from a
+# user's input file.
+# -----------------------------------------------------------------------------
+class LexerReflect(object):
+    def __init__(self, ldict, log=None, reflags=0):
+        self.ldict      = ldict
+        self.error_func = None
+        self.tokens     = []
+        self.reflags    = reflags
+        self.stateinfo  = {'INITIAL': 'inclusive'}
+        self.modules    = set()
+        self.error      = False
+        self.log        = PlyLogger(sys.stderr) if log is None else log
+
+    # Get all of the basic information
+    def get_all(self):
+        self.get_tokens()
+        self.get_literals()
+        self.get_states()
+        self.get_rules()
+
+    # Validate all of the information
+    def validate_all(self):
+        self.validate_tokens()
+        self.validate_literals()
+        self.validate_rules()
+        return self.error
+
+    # Get the tokens map
+    def get_tokens(self):
+        tokens = self.ldict.get('tokens', None)
+        if not tokens:
+            self.log.error('No token list is defined')
+            self.error = True
+            return
+
+        if not isinstance(tokens, (list, tuple)):
+            self.log.error('tokens must be a list or tuple')
+            self.error = True
+            return
+
+        if not tokens:
+            self.log.error('tokens is empty')
+            self.error = True
+            return
+
+        self.tokens = tokens
+
+    # Validate the tokens
+    def validate_tokens(self):
+        terminals = {}
+        for n in self.tokens:
+            if not _is_identifier.match(n):
+                self.log.error("Bad token name '%s'", n)
+                self.error = True
+            if n in terminals:
+                self.log.warning("Token '%s' multiply defined", n)
+            terminals[n] = 1
+
+    # Get the literals specifier
+    def get_literals(self):
+        self.literals = self.ldict.get('literals', '')
+        if not self.literals:
+            self.literals = ''
+
+    # Validate literals
+    def validate_literals(self):
+        try:
+            for c in self.literals:
+                if not isinstance(c, StringTypes) or len(c) > 1:
+                    self.log.error('Invalid literal %s. Must be a single character', repr(c))
+                    self.error = True
+
+        except TypeError:
+            self.log.error('Invalid literals specification. literals must be a sequence of characters')
+            self.error = True
+
+    def get_states(self):
+        self.states = self.ldict.get('states', None)
+        # Build statemap
+        if self.states:
+            if not isinstance(self.states, (tuple, list)):
+                self.log.error('states must be defined as a tuple or list')
+                self.error = True
+            else:
+                for s in self.states:
+                    if not isinstance(s, tuple) or len(s) != 2:
+                        self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s))
+                        self.error = True
+                        continue
+                    name, statetype = s
+                    if not isinstance(name, StringTypes):
+                        self.log.error('State name %s must be a string', repr(name))
+                        self.error = True
+                        continue
+                    if not (statetype == 'inclusive' or statetype == 'exclusive'):
+                        self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name)
+                        self.error = True
+                        continue
+                    if name in self.stateinfo:
+                        self.log.error("State '%s' already defined", name)
+                        self.error = True
+                        continue
+                    self.stateinfo[name] = statetype
+
+    # Get all of the symbols with a t_ prefix and sort them into various
+    # categories (functions, strings, error functions, and ignore characters)
+
+    def get_rules(self):
+        tsymbols = [f for f in self.ldict if f[:2] == 't_']
+
+        # Now build up a list of functions and a list of strings
+        self.toknames = {}        # Mapping of symbols to token names
+        self.funcsym  = {}        # Symbols defined as functions
+        self.strsym   = {}        # Symbols defined as strings
+        self.ignore   = {}        # Ignore strings by state
+        self.errorf   = {}        # Error functions by state
+        self.eoff     = {}        # EOF functions by state
+
+        for s in self.stateinfo:
+            self.funcsym[s] = []
+            self.strsym[s] = []
+
+        if len(tsymbols) == 0:
+            self.log.error('No rules of the form t_rulename are defined')
+            self.error = True
+            return
+
+        for f in tsymbols:
+            t = self.ldict[f]
+            states, tokname = _statetoken(f, self.stateinfo)
+            self.toknames[f] = tokname
+
+            if hasattr(t, '__call__'):
+                if tokname == 'error':
+                    for s in states:
+                        self.errorf[s] = t
+                elif tokname == 'eof':
+                    for s in states:
+                        self.eoff[s] = t
+                elif tokname == 'ignore':
+                    line = t.__code__.co_firstlineno
+                    file = t.__code__.co_filename
+                    self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__)
+                    self.error = True
+                else:
+                    for s in states:
+                        self.funcsym[s].append((f, t))
+            elif isinstance(t, StringTypes):
+                if tokname == 'ignore':
+                    for s in states:
+                        self.ignore[s] = t
+                    if '\\' in t:
+                        self.log.warning("%s contains a literal backslash '\\'", f)
+
+                elif tokname == 'error':
+                    self.log.error("Rule '%s' must be defined as a function", f)
+                    self.error = True
+                else:
+                    for s in states:
+                        self.strsym[s].append((f, t))
+            else:
+                self.log.error('%s not defined as a function or string', f)
+                self.error = True
+
+        # Sort the functions by line number
+        for f in self.funcsym.values():
+            f.sort(key=lambda x: x[1].__code__.co_firstlineno)
+
+        # Sort the strings by regular expression length
+        for s in self.strsym.values():
+            s.sort(key=lambda x: len(x[1]), reverse=True)
+
+    # Validate all of the t_rules collected
+    def validate_rules(self):
+        for state in self.stateinfo:
+            # Validate all rules defined by functions
+
+            for fname, f in self.funcsym[state]:
+                line = f.__code__.co_firstlineno
+                file = f.__code__.co_filename
+                module = inspect.getmodule(f)
+                self.modules.add(module)
+
+                tokname = self.toknames[fname]
+                if isinstance(f, types.MethodType):
+                    reqargs = 2
+                else:
+                    reqargs = 1
+                nargs = f.__code__.co_argcount
+                if nargs > reqargs:
+                    self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
+                    self.error = True
+                    continue
+
+                if nargs < reqargs:
+                    self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
+                    self.error = True
+                    continue
+
+                if not _get_regex(f):
+                    self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__)
+                    self.error = True
+                    continue
+
+                try:
+                    c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags)
+                    if c.match(''):
+                        self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__)
+                        self.error = True
+                except re.error as e:
+                    self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e)
+                    if '#' in _get_regex(f):
+                        self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__)
+                    self.error = True
+
+            # Validate all rules defined by strings
+            for name, r in self.strsym[state]:
+                tokname = self.toknames[name]
+                if tokname == 'error':
+                    self.log.error("Rule '%s' must be defined as a function", name)
+                    self.error = True
+                    continue
+
+                if tokname not in self.tokens and tokname.find('ignore_') < 0:
+                    self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname)
+                    self.error = True
+                    continue
+
+                try:
+                    c = re.compile('(?P<%s>%s)' % (name, r), self.reflags)
+                    if (c.match('')):
+                        self.log.error("Regular expression for rule '%s' matches empty string", name)
+                        self.error = True
+                except re.error as e:
+                    self.log.error("Invalid regular expression for rule '%s'. %s", name, e)
+                    if '#' in r:
+                        self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name)
+                    self.error = True
+
+            if not self.funcsym[state] and not self.strsym[state]:
+                self.log.error("No rules defined for state '%s'", state)
+                self.error = True
+
+            # Validate the error function
+            efunc = self.errorf.get(state, None)
+            if efunc:
+                f = efunc
+                line = f.__code__.co_firstlineno
+                file = f.__code__.co_filename
+                module = inspect.getmodule(f)
+                self.modules.add(module)
+
+                if isinstance(f, types.MethodType):
+                    reqargs = 2
+                else:
+                    reqargs = 1
+                nargs = f.__code__.co_argcount
+                if nargs > reqargs:
+                    self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
+                    self.error = True
+
+                if nargs < reqargs:
+                    self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
+                    self.error = True
+
+        for module in self.modules:
+            self.validate_module(module)
+
+    # -----------------------------------------------------------------------------
+    # validate_module()
+    #
+    # This checks to see if there are duplicated t_rulename() functions or strings
+    # in the parser input file.  This is done using a simple regular expression
+    # match on each line in the source code of the given module.
+    # -----------------------------------------------------------------------------
+
+    def validate_module(self, module):
+        try:
+            lines, linen = inspect.getsourcelines(module)
+        except IOError:
+            return
+
+        fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
+        sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
+
+        counthash = {}
+        linen += 1
+        for line in lines:
+            m = fre.match(line)
+            if not m:
+                m = sre.match(line)
+            if m:
+                name = m.group(1)
+                prev = counthash.get(name)
+                if not prev:
+                    counthash[name] = linen
+                else:
+                    filename = inspect.getsourcefile(module)
+                    self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev)
+                    self.error = True
+            linen += 1
+
+# -----------------------------------------------------------------------------
+# lex(module)
+#
+# Build all of the regular expression rules from definitions in the supplied module
+# -----------------------------------------------------------------------------
+def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab',
+        reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None):
+
+    if lextab is None:
+        lextab = 'lextab'
+
+    global lexer
+
+    ldict = None
+    stateinfo  = {'INITIAL': 'inclusive'}
+    lexobj = Lexer()
+    lexobj.lexoptimize = optimize
+    global token, input
+
+    if errorlog is None:
+        errorlog = PlyLogger(sys.stderr)
+
+    if debug:
+        if debuglog is None:
+            debuglog = PlyLogger(sys.stderr)
+
+    # Get the module dictionary used for the lexer
+    if object:
+        module = object
+
+    # Get the module dictionary used for the parser
+    if module:
+        _items = [(k, getattr(module, k)) for k in dir(module)]
+        ldict = dict(_items)
+        # If no __file__ attribute is available, try to obtain it from the __module__ instead
+        if '__file__' not in ldict:
+            ldict['__file__'] = sys.modules[ldict['__module__']].__file__
+    else:
+        ldict = get_caller_module_dict(2)
+
+    # Determine if the module is package of a package or not.
+    # If so, fix the tabmodule setting so that tables load correctly
+    pkg = ldict.get('__package__')
+    if pkg and isinstance(lextab, str):
+        if '.' not in lextab:
+            lextab = pkg + '.' + lextab
+
+    # Collect parser information from the dictionary
+    linfo = LexerReflect(ldict, log=errorlog, reflags=reflags)
+    linfo.get_all()
+    if not optimize:
+        if linfo.validate_all():
+            raise SyntaxError("Can't build lexer")
+
+    if optimize and lextab:
+        try:
+            lexobj.readtab(lextab, ldict)
+            token = lexobj.token
+            input = lexobj.input
+            lexer = lexobj
+            return lexobj
+
+        except ImportError:
+            pass
+
+    # Dump some basic debugging information
+    if debug:
+        debuglog.info('lex: tokens   = %r', linfo.tokens)
+        debuglog.info('lex: literals = %r', linfo.literals)
+        debuglog.info('lex: states   = %r', linfo.stateinfo)
+
+    # Build a dictionary of valid token names
+    lexobj.lextokens = set()
+    for n in linfo.tokens:
+        lexobj.lextokens.add(n)
+
+    # Get literals specification
+    if isinstance(linfo.literals, (list, tuple)):
+        lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
+    else:
+        lexobj.lexliterals = linfo.literals
+
+    lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals)
+
+    # Get the stateinfo dictionary
+    stateinfo = linfo.stateinfo
+
+    regexs = {}
+    # Build the master regular expressions
+    for state in stateinfo:
+        regex_list = []
+
+        # Add rules defined by functions first
+        for fname, f in linfo.funcsym[state]:
+            line = f.__code__.co_firstlineno
+            file = f.__code__.co_filename
+            regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f)))
+            if debug:
+                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state)
+
+        # Now add all of the simple rules
+        for name, r in linfo.strsym[state]:
+            regex_list.append('(?P<%s>%s)' % (name, r))
+            if debug:
+                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state)
+
+        regexs[state] = regex_list
+
+    # Build the master regular expressions
+
+    if debug:
+        debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====')
+
+    for state in regexs:
+        lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames)
+        lexobj.lexstatere[state] = lexre
+        lexobj.lexstateretext[state] = re_text
+        lexobj.lexstaterenames[state] = re_names
+        if debug:
+            for i, text in enumerate(re_text):
+                debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text)
+
+    # For inclusive states, we need to add the regular expressions from the INITIAL state
+    for state, stype in stateinfo.items():
+        if state != 'INITIAL' and stype == 'inclusive':
+            lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
+            lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
+            lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
+
+    lexobj.lexstateinfo = stateinfo
+    lexobj.lexre = lexobj.lexstatere['INITIAL']
+    lexobj.lexretext = lexobj.lexstateretext['INITIAL']
+    lexobj.lexreflags = reflags
+
+    # Set up ignore variables
+    lexobj.lexstateignore = linfo.ignore
+    lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '')
+
+    # Set up error functions
+    lexobj.lexstateerrorf = linfo.errorf
+    lexobj.lexerrorf = linfo.errorf.get('INITIAL', None)
+    if not lexobj.lexerrorf:
+        errorlog.warning('No t_error rule is defined')
+
+    # Set up eof functions
+    lexobj.lexstateeoff = linfo.eoff
+    lexobj.lexeoff = linfo.eoff.get('INITIAL', None)
+
+    # Check state information for ignore and error rules
+    for s, stype in stateinfo.items():
+        if stype == 'exclusive':
+            if s not in linfo.errorf:
+                errorlog.warning("No error rule is defined for exclusive state '%s'", s)
+            if s not in linfo.ignore and lexobj.lexignore:
+                errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
+        elif stype == 'inclusive':
+            if s not in linfo.errorf:
+                linfo.errorf[s] = linfo.errorf.get('INITIAL', None)
+            if s not in linfo.ignore:
+                linfo.ignore[s] = linfo.ignore.get('INITIAL', '')
+
+    # Create global versions of the token() and input() functions
+    token = lexobj.token
+    input = lexobj.input
+    lexer = lexobj
+
+    # If in optimize mode, we write the lextab
+    if lextab and optimize:
+        if outputdir is None:
+            # If no output directory is set, the location of the output files
+            # is determined according to the following rules:
+            #     - If lextab specifies a package, files go into that package directory
+            #     - Otherwise, files go in the same directory as the specifying module
+            if isinstance(lextab, types.ModuleType):
+                srcfile = lextab.__file__
+            else:
+                if '.' not in lextab:
+                    srcfile = ldict['__file__']
+                else:
+                    parts = lextab.split('.')
+                    pkgname = '.'.join(parts[:-1])
+                    exec('import %s' % pkgname)
+                    srcfile = getattr(sys.modules[pkgname], '__file__', '')
+            outputdir = os.path.dirname(srcfile)
+        try:
+            lexobj.writetab(lextab, outputdir)
+        except IOError as e:
+            errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e))
+
+    return lexobj
+
+# -----------------------------------------------------------------------------
+# runmain()
+#
+# This runs the lexer as a main program
+# -----------------------------------------------------------------------------
+
+def runmain(lexer=None, data=None):
+    if not data:
+        try:
+            filename = sys.argv[1]
+            f = open(filename)
+            data = f.read()
+            f.close()
+        except IndexError:
+            sys.stdout.write('Reading from standard input (type EOF to end):\n')
+            data = sys.stdin.read()
+
+    if lexer:
+        _input = lexer.input
+    else:
+        _input = input
+    _input(data)
+    if lexer:
+        _token = lexer.token
+    else:
+        _token = token
+
+    while True:
+        tok = _token()
+        if not tok:
+            break
+        sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos))
+
+# -----------------------------------------------------------------------------
+# @TOKEN(regex)
+#
+# This decorator function can be used to set the regex expression on a function
+# when its docstring might need to be set in an alternative way
+# -----------------------------------------------------------------------------
+
+def TOKEN(r):
+    def set_regex(f):
+        if hasattr(r, '__call__'):
+            f.regex = _get_regex(r)
+        else:
+            f.regex = r
+        return f
+    return set_regex
+
+# Alternative spelling of the TOKEN decorator
+Token = TOKEN
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ply/yacc.py b/.venv/lib/python3.8/site-packages/pycparser/ply/yacc.py
new file mode 100644
index 0000000000000000000000000000000000000000..20b4f2863cc7193ad4b779a30375c865495fa50c
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ply/yacc.py
@@ -0,0 +1,3494 @@
+# -----------------------------------------------------------------------------
+# ply: yacc.py
+#
+# Copyright (C) 2001-2017
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+#   endorse or promote products derived from this software without
+#  specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+#
+# This implements an LR parser that is constructed from grammar rules defined
+# as Python functions. The grammer is specified by supplying the BNF inside
+# Python documentation strings.  The inspiration for this technique was borrowed
+# from John Aycock's Spark parsing system.  PLY might be viewed as cross between
+# Spark and the GNU bison utility.
+#
+# The current implementation is only somewhat object-oriented. The
+# LR parser itself is defined in terms of an object (which allows multiple
+# parsers to co-exist).  However, most of the variables used during table
+# construction are defined in terms of global variables.  Users shouldn't
+# notice unless they are trying to define multiple parsers at the same
+# time using threads (in which case they should have their head examined).
+#
+# This implementation supports both SLR and LALR(1) parsing.  LALR(1)
+# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu),
+# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles,
+# Techniques, and Tools" (The Dragon Book).  LALR(1) has since been replaced
+# by the more efficient DeRemer and Pennello algorithm.
+#
+# :::::::: WARNING :::::::
+#
+# Construction of LR parsing tables is fairly complicated and expensive.
+# To make this module run fast, a *LOT* of work has been put into
+# optimization---often at the expensive of readability and what might
+# consider to be good Python "coding style."   Modify the code at your
+# own risk!
+# ----------------------------------------------------------------------------
+
+import re
+import types
+import sys
+import os.path
+import inspect
+import base64
+import warnings
+
+__version__    = '3.10'
+__tabversion__ = '3.10'
+
+#-----------------------------------------------------------------------------
+#                     === User configurable parameters ===
+#
+# Change these to modify the default behavior of yacc (if you wish)
+#-----------------------------------------------------------------------------
+
+yaccdebug   = True             # Debugging mode.  If set, yacc generates a
+                               # a 'parser.out' file in the current directory
+
+debug_file  = 'parser.out'     # Default name of the debugging file
+tab_module  = 'parsetab'       # Default name of the table module
+default_lr  = 'LALR'           # Default LR table generation method
+
+error_count = 3                # Number of symbols that must be shifted to leave recovery mode
+
+yaccdevel   = False            # Set to True if developing yacc.  This turns off optimized
+                               # implementations of certain functions.
+
+resultlimit = 40               # Size limit of results when running in debug mode.
+
+pickle_protocol = 0            # Protocol to use when writing pickle files
+
+# String type-checking compatibility
+if sys.version_info[0] < 3:
+    string_types = basestring
+else:
+    string_types = str
+
+MAXINT = sys.maxsize
+
+# This object is a stand-in for a logging object created by the
+# logging module.   PLY will use this by default to create things
+# such as the parser.out file.  If a user wants more detailed
+# information, they can create their own logging object and pass
+# it into PLY.
+
+class PlyLogger(object):
+    def __init__(self, f):
+        self.f = f
+
+    def debug(self, msg, *args, **kwargs):
+        self.f.write((msg % args) + '\n')
+
+    info = debug
+
+    def warning(self, msg, *args, **kwargs):
+        self.f.write('WARNING: ' + (msg % args) + '\n')
+
+    def error(self, msg, *args, **kwargs):
+        self.f.write('ERROR: ' + (msg % args) + '\n')
+
+    critical = debug
+
+# Null logger is used when no output is generated. Does nothing.
+class NullLogger(object):
+    def __getattribute__(self, name):
+        return self
+
+    def __call__(self, *args, **kwargs):
+        return self
+
+# Exception raised for yacc-related errors
+class YaccError(Exception):
+    pass
+
+# Format the result message that the parser produces when running in debug mode.
+def format_result(r):
+    repr_str = repr(r)
+    if '\n' in repr_str:
+        repr_str = repr(repr_str)
+    if len(repr_str) > resultlimit:
+        repr_str = repr_str[:resultlimit] + ' ...'
+    result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str)
+    return result
+
+# Format stack entries when the parser is running in debug mode
+def format_stack_entry(r):
+    repr_str = repr(r)
+    if '\n' in repr_str:
+        repr_str = repr(repr_str)
+    if len(repr_str) < 16:
+        return repr_str
+    else:
+        return '<%s @ 0x%x>' % (type(r).__name__, id(r))
+
+# Panic mode error recovery support.   This feature is being reworked--much of the
+# code here is to offer a deprecation/backwards compatible transition
+
+_errok = None
+_token = None
+_restart = None
+_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error().
+Instead, invoke the methods on the associated parser instance:
+
+    def p_error(p):
+        ...
+        # Use parser.errok(), parser.token(), parser.restart()
+        ...
+
+    parser = yacc.yacc()
+'''
+
+def errok():
+    warnings.warn(_warnmsg)
+    return _errok()
+
+def restart():
+    warnings.warn(_warnmsg)
+    return _restart()
+
+def token():
+    warnings.warn(_warnmsg)
+    return _token()
+
+# Utility function to call the p_error() function with some deprecation hacks
+def call_errorfunc(errorfunc, token, parser):
+    global _errok, _token, _restart
+    _errok = parser.errok
+    _token = parser.token
+    _restart = parser.restart
+    r = errorfunc(token)
+    try:
+        del _errok, _token, _restart
+    except NameError:
+        pass
+    return r
+
+#-----------------------------------------------------------------------------
+#                        ===  LR Parsing Engine ===
+#
+# The following classes are used for the LR parser itself.  These are not
+# used during table construction and are independent of the actual LR
+# table generation algorithm
+#-----------------------------------------------------------------------------
+
+# This class is used to hold non-terminal grammar symbols during parsing.
+# It normally has the following attributes set:
+#        .type       = Grammar symbol type
+#        .value      = Symbol value
+#        .lineno     = Starting line number
+#        .endlineno  = Ending line number (optional, set automatically)
+#        .lexpos     = Starting lex position
+#        .endlexpos  = Ending lex position (optional, set automatically)
+
+class YaccSymbol:
+    def __str__(self):
+        return self.type
+
+    def __repr__(self):
+        return str(self)
+
+# This class is a wrapper around the objects actually passed to each
+# grammar rule.   Index lookup and assignment actually assign the
+# .value attribute of the underlying YaccSymbol object.
+# The lineno() method returns the line number of a given
+# item (or 0 if not defined).   The linespan() method returns
+# a tuple of (startline,endline) representing the range of lines
+# for a symbol.  The lexspan() method returns a tuple (lexpos,endlexpos)
+# representing the range of positional information for a symbol.
+
+class YaccProduction:
+    def __init__(self, s, stack=None):
+        self.slice = s
+        self.stack = stack
+        self.lexer = None
+        self.parser = None
+
+    def __getitem__(self, n):
+        if isinstance(n, slice):
+            return [s.value for s in self.slice[n]]
+        elif n >= 0:
+            return self.slice[n].value
+        else:
+            return self.stack[n].value
+
+    def __setitem__(self, n, v):
+        self.slice[n].value = v
+
+    def __getslice__(self, i, j):
+        return [s.value for s in self.slice[i:j]]
+
+    def __len__(self):
+        return len(self.slice)
+
+    def lineno(self, n):
+        return getattr(self.slice[n], 'lineno', 0)
+
+    def set_lineno(self, n, lineno):
+        self.slice[n].lineno = lineno
+
+    def linespan(self, n):
+        startline = getattr(self.slice[n], 'lineno', 0)
+        endline = getattr(self.slice[n], 'endlineno', startline)
+        return startline, endline
+
+    def lexpos(self, n):
+        return getattr(self.slice[n], 'lexpos', 0)
+
+    def lexspan(self, n):
+        startpos = getattr(self.slice[n], 'lexpos', 0)
+        endpos = getattr(self.slice[n], 'endlexpos', startpos)
+        return startpos, endpos
+
+    def error(self):
+        raise SyntaxError
+
+# -----------------------------------------------------------------------------
+#                               == LRParser ==
+#
+# The LR Parsing engine.
+# -----------------------------------------------------------------------------
+
+class LRParser:
+    def __init__(self, lrtab, errorf):
+        self.productions = lrtab.lr_productions
+        self.action = lrtab.lr_action
+        self.goto = lrtab.lr_goto
+        self.errorfunc = errorf
+        self.set_defaulted_states()
+        self.errorok = True
+
+    def errok(self):
+        self.errorok = True
+
+    def restart(self):
+        del self.statestack[:]
+        del self.symstack[:]
+        sym = YaccSymbol()
+        sym.type = '$end'
+        self.symstack.append(sym)
+        self.statestack.append(0)
+
+    # Defaulted state support.
+    # This method identifies parser states where there is only one possible reduction action.
+    # For such states, the parser can make a choose to make a rule reduction without consuming
+    # the next look-ahead token.  This delayed invocation of the tokenizer can be useful in
+    # certain kinds of advanced parsing situations where the lexer and parser interact with
+    # each other or change states (i.e., manipulation of scope, lexer states, etc.).
+    #
+    # See:  https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions
+    def set_defaulted_states(self):
+        self.defaulted_states = {}
+        for state, actions in self.action.items():
+            rules = list(actions.values())
+            if len(rules) == 1 and rules[0] < 0:
+                self.defaulted_states[state] = rules[0]
+
+    def disable_defaulted_states(self):
+        self.defaulted_states = {}
+
+    def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        if debug or yaccdevel:
+            if isinstance(debug, int):
+                debug = PlyLogger(sys.stderr)
+            return self.parsedebug(input, lexer, debug, tracking, tokenfunc)
+        elif tracking:
+            return self.parseopt(input, lexer, debug, tracking, tokenfunc)
+        else:
+            return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
+
+
+    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    # parsedebug().
+    #
+    # This is the debugging enabled version of parse().  All changes made to the
+    # parsing engine should be made here.   Optimized versions of this function
+    # are automatically created by the ply/ygen.py script.  This script cuts out
+    # sections enclosed in markers such as this:
+    #
+    #      #--! DEBUG
+    #      statements
+    #      #--! DEBUG
+    #
+    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+    def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        #--! parsedebug-start
+        lookahead = None                         # Current lookahead symbol
+        lookaheadstack = []                      # Stack of lookahead symbols
+        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
+        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
+        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
+        defaulted_states = self.defaulted_states # Local reference to defaulted states
+        pslice  = YaccProduction(None)           # Production object passed to grammar rules
+        errorcount = 0                           # Used during error recovery
+
+        #--! DEBUG
+        debug.info('PLY: PARSE DEBUG START')
+        #--! DEBUG
+
+        # If no lexer was given, we will try to use the lex module
+        if not lexer:
+            from . import lex
+            lexer = lex.lexer
+
+        # Set up the lexer and parser objects on pslice
+        pslice.lexer = lexer
+        pslice.parser = self
+
+        # If input was supplied, pass to lexer
+        if input is not None:
+            lexer.input(input)
+
+        if tokenfunc is None:
+            # Tokenize function
+            get_token = lexer.token
+        else:
+            get_token = tokenfunc
+
+        # Set the parser() token method (sometimes used in error recovery)
+        self.token = get_token
+
+        # Set up the state and symbol stacks
+
+        statestack = []                # Stack of parsing states
+        self.statestack = statestack
+        symstack   = []                # Stack of grammar symbols
+        self.symstack = symstack
+
+        pslice.stack = symstack         # Put in the production
+        errtoken   = None               # Err token
+
+        # The start state is assumed to be (0,$end)
+
+        statestack.append(0)
+        sym = YaccSymbol()
+        sym.type = '$end'
+        symstack.append(sym)
+        state = 0
+        while True:
+            # Get the next symbol on the input.  If a lookahead symbol
+            # is already set, we just use that. Otherwise, we'll pull
+            # the next token off of the lookaheadstack or from the lexer
+
+            #--! DEBUG
+            debug.debug('')
+            debug.debug('State  : %s', state)
+            #--! DEBUG
+
+            if state not in defaulted_states:
+                if not lookahead:
+                    if not lookaheadstack:
+                        lookahead = get_token()     # Get the next token
+                    else:
+                        lookahead = lookaheadstack.pop()
+                    if not lookahead:
+                        lookahead = YaccSymbol()
+                        lookahead.type = '$end'
+
+                # Check the action table
+                ltype = lookahead.type
+                t = actions[state].get(ltype)
+            else:
+                t = defaulted_states[state]
+                #--! DEBUG
+                debug.debug('Defaulted state %s: Reduce using %d', state, -t)
+                #--! DEBUG
+
+            #--! DEBUG
+            debug.debug('Stack  : %s',
+                        ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+            #--! DEBUG
+
+            if t is not None:
+                if t > 0:
+                    # shift a symbol on the stack
+                    statestack.append(t)
+                    state = t
+
+                    #--! DEBUG
+                    debug.debug('Action : Shift and goto state %s', t)
+                    #--! DEBUG
+
+                    symstack.append(lookahead)
+                    lookahead = None
+
+                    # Decrease error count on successful shift
+                    if errorcount:
+                        errorcount -= 1
+                    continue
+
+                if t < 0:
+                    # reduce a symbol on the stack, emit a production
+                    p = prod[-t]
+                    pname = p.name
+                    plen  = p.len
+
+                    # Get production function
+                    sym = YaccSymbol()
+                    sym.type = pname       # Production name
+                    sym.value = None
+
+                    #--! DEBUG
+                    if plen:
+                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str,
+                                   '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']',
+                                   goto[statestack[-1-plen]][pname])
+                    else:
+                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [],
+                                   goto[statestack[-1]][pname])
+
+                    #--! DEBUG
+
+                    if plen:
+                        targ = symstack[-plen-1:]
+                        targ[0] = sym
+
+                        #--! TRACKING
+                        if tracking:
+                            t1 = targ[1]
+                            sym.lineno = t1.lineno
+                            sym.lexpos = t1.lexpos
+                            t1 = targ[-1]
+                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
+                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
+                        #--! TRACKING
+
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                        # The code enclosed in this section is duplicated
+                        # below as a performance optimization.  Make sure
+                        # changes get made in both locations.
+
+                        pslice.slice = targ
+
+                        try:
+                            # Call the grammar rule with our special slice object
+                            del symstack[-plen:]
+                            self.state = state
+                            p.callable(pslice)
+                            del statestack[-plen:]
+                            #--! DEBUG
+                            debug.info('Result : %s', format_result(pslice[0]))
+                            #--! DEBUG
+                            symstack.append(sym)
+                            state = goto[statestack[-1]][pname]
+                            statestack.append(state)
+                        except SyntaxError:
+                            # If an error was set. Enter error recovery state
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
+                            statestack.pop()                    # Pop back one state (before the reduce)
+                            state = statestack[-1]
+                            sym.type = 'error'
+                            sym.value = 'error'
+                            lookahead = sym
+                            errorcount = error_count
+                            self.errorok = False
+
+                        continue
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+                    else:
+
+                        #--! TRACKING
+                        if tracking:
+                            sym.lineno = lexer.lineno
+                            sym.lexpos = lexer.lexpos
+                        #--! TRACKING
+
+                        targ = [sym]
+
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                        # The code enclosed in this section is duplicated
+                        # above as a performance optimization.  Make sure
+                        # changes get made in both locations.
+
+                        pslice.slice = targ
+
+                        try:
+                            # Call the grammar rule with our special slice object
+                            self.state = state
+                            p.callable(pslice)
+                            #--! DEBUG
+                            debug.info('Result : %s', format_result(pslice[0]))
+                            #--! DEBUG
+                            symstack.append(sym)
+                            state = goto[statestack[-1]][pname]
+                            statestack.append(state)
+                        except SyntaxError:
+                            # If an error was set. Enter error recovery state
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            statestack.pop()                    # Pop back one state (before the reduce)
+                            state = statestack[-1]
+                            sym.type = 'error'
+                            sym.value = 'error'
+                            lookahead = sym
+                            errorcount = error_count
+                            self.errorok = False
+
+                        continue
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+                if t == 0:
+                    n = symstack[-1]
+                    result = getattr(n, 'value', None)
+                    #--! DEBUG
+                    debug.info('Done   : Returning %s', format_result(result))
+                    debug.info('PLY: PARSE DEBUG END')
+                    #--! DEBUG
+                    return result
+
+            if t is None:
+
+                #--! DEBUG
+                debug.error('Error  : %s',
+                            ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+                #--! DEBUG
+
+                # We have some kind of parsing error here.  To handle
+                # this, we are going to push the current token onto
+                # the tokenstack and replace it with an 'error' token.
+                # If there are any synchronization rules, they may
+                # catch it.
+                #
+                # In addition to pushing the error token, we call call
+                # the user defined p_error() function if this is the
+                # first syntax error.  This function is only called if
+                # errorcount == 0.
+                if errorcount == 0 or self.errorok:
+                    errorcount = error_count
+                    self.errorok = False
+                    errtoken = lookahead
+                    if errtoken.type == '$end':
+                        errtoken = None               # End of file!
+                    if self.errorfunc:
+                        if errtoken and not hasattr(errtoken, 'lexer'):
+                            errtoken.lexer = lexer
+                        self.state = state
+                        tok = call_errorfunc(self.errorfunc, errtoken, self)
+                        if self.errorok:
+                            # User must have done some kind of panic
+                            # mode recovery on their own.  The
+                            # returned token is the next lookahead
+                            lookahead = tok
+                            errtoken = None
+                            continue
+                    else:
+                        if errtoken:
+                            if hasattr(errtoken, 'lineno'):
+                                lineno = lookahead.lineno
+                            else:
+                                lineno = 0
+                            if lineno:
+                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
+                            else:
+                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
+                        else:
+                            sys.stderr.write('yacc: Parse error in input. EOF\n')
+                            return
+
+                else:
+                    errorcount = error_count
+
+                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
+                # entire parse has been rolled back and we're completely hosed.   The token is
+                # discarded and we just keep going.
+
+                if len(statestack) <= 1 and lookahead.type != '$end':
+                    lookahead = None
+                    errtoken = None
+                    state = 0
+                    # Nuke the pushback stack
+                    del lookaheadstack[:]
+                    continue
+
+                # case 2: the statestack has a couple of entries on it, but we're
+                # at the end of the file. nuke the top entry and generate an error token
+
+                # Start nuking entries on the stack
+                if lookahead.type == '$end':
+                    # Whoa. We're really hosed here. Bail out
+                    return
+
+                if lookahead.type != 'error':
+                    sym = symstack[-1]
+                    if sym.type == 'error':
+                        # Hmmm. Error is on top of stack, we'll just nuke input
+                        # symbol and continue
+                        #--! TRACKING
+                        if tracking:
+                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
+                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
+                        #--! TRACKING
+                        lookahead = None
+                        continue
+
+                    # Create the error symbol for the first time and make it the new lookahead symbol
+                    t = YaccSymbol()
+                    t.type = 'error'
+
+                    if hasattr(lookahead, 'lineno'):
+                        t.lineno = t.endlineno = lookahead.lineno
+                    if hasattr(lookahead, 'lexpos'):
+                        t.lexpos = t.endlexpos = lookahead.lexpos
+                    t.value = lookahead
+                    lookaheadstack.append(lookahead)
+                    lookahead = t
+                else:
+                    sym = symstack.pop()
+                    #--! TRACKING
+                    if tracking:
+                        lookahead.lineno = sym.lineno
+                        lookahead.lexpos = sym.lexpos
+                    #--! TRACKING
+                    statestack.pop()
+                    state = statestack[-1]
+
+                continue
+
+            # Call an error function here
+            raise RuntimeError('yacc: internal parser error!!!\n')
+
+        #--! parsedebug-end
+
+    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    # parseopt().
+    #
+    # Optimized version of parse() method.  DO NOT EDIT THIS CODE DIRECTLY!
+    # This code is automatically generated by the ply/ygen.py script. Make
+    # changes to the parsedebug() method instead.
+    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+    def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        #--! parseopt-start
+        lookahead = None                         # Current lookahead symbol
+        lookaheadstack = []                      # Stack of lookahead symbols
+        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
+        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
+        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
+        defaulted_states = self.defaulted_states # Local reference to defaulted states
+        pslice  = YaccProduction(None)           # Production object passed to grammar rules
+        errorcount = 0                           # Used during error recovery
+
+
+        # If no lexer was given, we will try to use the lex module
+        if not lexer:
+            from . import lex
+            lexer = lex.lexer
+
+        # Set up the lexer and parser objects on pslice
+        pslice.lexer = lexer
+        pslice.parser = self
+
+        # If input was supplied, pass to lexer
+        if input is not None:
+            lexer.input(input)
+
+        if tokenfunc is None:
+            # Tokenize function
+            get_token = lexer.token
+        else:
+            get_token = tokenfunc
+
+        # Set the parser() token method (sometimes used in error recovery)
+        self.token = get_token
+
+        # Set up the state and symbol stacks
+
+        statestack = []                # Stack of parsing states
+        self.statestack = statestack
+        symstack   = []                # Stack of grammar symbols
+        self.symstack = symstack
+
+        pslice.stack = symstack         # Put in the production
+        errtoken   = None               # Err token
+
+        # The start state is assumed to be (0,$end)
+
+        statestack.append(0)
+        sym = YaccSymbol()
+        sym.type = '$end'
+        symstack.append(sym)
+        state = 0
+        while True:
+            # Get the next symbol on the input.  If a lookahead symbol
+            # is already set, we just use that. Otherwise, we'll pull
+            # the next token off of the lookaheadstack or from the lexer
+
+
+            if state not in defaulted_states:
+                if not lookahead:
+                    if not lookaheadstack:
+                        lookahead = get_token()     # Get the next token
+                    else:
+                        lookahead = lookaheadstack.pop()
+                    if not lookahead:
+                        lookahead = YaccSymbol()
+                        lookahead.type = '$end'
+
+                # Check the action table
+                ltype = lookahead.type
+                t = actions[state].get(ltype)
+            else:
+                t = defaulted_states[state]
+
+
+            if t is not None:
+                if t > 0:
+                    # shift a symbol on the stack
+                    statestack.append(t)
+                    state = t
+
+
+                    symstack.append(lookahead)
+                    lookahead = None
+
+                    # Decrease error count on successful shift
+                    if errorcount:
+                        errorcount -= 1
+                    continue
+
+                if t < 0:
+                    # reduce a symbol on the stack, emit a production
+                    p = prod[-t]
+                    pname = p.name
+                    plen  = p.len
+
+                    # Get production function
+                    sym = YaccSymbol()
+                    sym.type = pname       # Production name
+                    sym.value = None
+
+
+                    if plen:
+                        targ = symstack[-plen-1:]
+                        targ[0] = sym
+
+                        #--! TRACKING
+                        if tracking:
+                            t1 = targ[1]
+                            sym.lineno = t1.lineno
+                            sym.lexpos = t1.lexpos
+                            t1 = targ[-1]
+                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
+                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
+                        #--! TRACKING
+
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                        # The code enclosed in this section is duplicated
+                        # below as a performance optimization.  Make sure
+                        # changes get made in both locations.
+
+                        pslice.slice = targ
+
+                        try:
+                            # Call the grammar rule with our special slice object
+                            del symstack[-plen:]
+                            self.state = state
+                            p.callable(pslice)
+                            del statestack[-plen:]
+                            symstack.append(sym)
+                            state = goto[statestack[-1]][pname]
+                            statestack.append(state)
+                        except SyntaxError:
+                            # If an error was set. Enter error recovery state
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
+                            statestack.pop()                    # Pop back one state (before the reduce)
+                            state = statestack[-1]
+                            sym.type = 'error'
+                            sym.value = 'error'
+                            lookahead = sym
+                            errorcount = error_count
+                            self.errorok = False
+
+                        continue
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+                    else:
+
+                        #--! TRACKING
+                        if tracking:
+                            sym.lineno = lexer.lineno
+                            sym.lexpos = lexer.lexpos
+                        #--! TRACKING
+
+                        targ = [sym]
+
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                        # The code enclosed in this section is duplicated
+                        # above as a performance optimization.  Make sure
+                        # changes get made in both locations.
+
+                        pslice.slice = targ
+
+                        try:
+                            # Call the grammar rule with our special slice object
+                            self.state = state
+                            p.callable(pslice)
+                            symstack.append(sym)
+                            state = goto[statestack[-1]][pname]
+                            statestack.append(state)
+                        except SyntaxError:
+                            # If an error was set. Enter error recovery state
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            statestack.pop()                    # Pop back one state (before the reduce)
+                            state = statestack[-1]
+                            sym.type = 'error'
+                            sym.value = 'error'
+                            lookahead = sym
+                            errorcount = error_count
+                            self.errorok = False
+
+                        continue
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+                if t == 0:
+                    n = symstack[-1]
+                    result = getattr(n, 'value', None)
+                    return result
+
+            if t is None:
+
+
+                # We have some kind of parsing error here.  To handle
+                # this, we are going to push the current token onto
+                # the tokenstack and replace it with an 'error' token.
+                # If there are any synchronization rules, they may
+                # catch it.
+                #
+                # In addition to pushing the error token, we call call
+                # the user defined p_error() function if this is the
+                # first syntax error.  This function is only called if
+                # errorcount == 0.
+                if errorcount == 0 or self.errorok:
+                    errorcount = error_count
+                    self.errorok = False
+                    errtoken = lookahead
+                    if errtoken.type == '$end':
+                        errtoken = None               # End of file!
+                    if self.errorfunc:
+                        if errtoken and not hasattr(errtoken, 'lexer'):
+                            errtoken.lexer = lexer
+                        self.state = state
+                        tok = call_errorfunc(self.errorfunc, errtoken, self)
+                        if self.errorok:
+                            # User must have done some kind of panic
+                            # mode recovery on their own.  The
+                            # returned token is the next lookahead
+                            lookahead = tok
+                            errtoken = None
+                            continue
+                    else:
+                        if errtoken:
+                            if hasattr(errtoken, 'lineno'):
+                                lineno = lookahead.lineno
+                            else:
+                                lineno = 0
+                            if lineno:
+                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
+                            else:
+                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
+                        else:
+                            sys.stderr.write('yacc: Parse error in input. EOF\n')
+                            return
+
+                else:
+                    errorcount = error_count
+
+                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
+                # entire parse has been rolled back and we're completely hosed.   The token is
+                # discarded and we just keep going.
+
+                if len(statestack) <= 1 and lookahead.type != '$end':
+                    lookahead = None
+                    errtoken = None
+                    state = 0
+                    # Nuke the pushback stack
+                    del lookaheadstack[:]
+                    continue
+
+                # case 2: the statestack has a couple of entries on it, but we're
+                # at the end of the file. nuke the top entry and generate an error token
+
+                # Start nuking entries on the stack
+                if lookahead.type == '$end':
+                    # Whoa. We're really hosed here. Bail out
+                    return
+
+                if lookahead.type != 'error':
+                    sym = symstack[-1]
+                    if sym.type == 'error':
+                        # Hmmm. Error is on top of stack, we'll just nuke input
+                        # symbol and continue
+                        #--! TRACKING
+                        if tracking:
+                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
+                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
+                        #--! TRACKING
+                        lookahead = None
+                        continue
+
+                    # Create the error symbol for the first time and make it the new lookahead symbol
+                    t = YaccSymbol()
+                    t.type = 'error'
+
+                    if hasattr(lookahead, 'lineno'):
+                        t.lineno = t.endlineno = lookahead.lineno
+                    if hasattr(lookahead, 'lexpos'):
+                        t.lexpos = t.endlexpos = lookahead.lexpos
+                    t.value = lookahead
+                    lookaheadstack.append(lookahead)
+                    lookahead = t
+                else:
+                    sym = symstack.pop()
+                    #--! TRACKING
+                    if tracking:
+                        lookahead.lineno = sym.lineno
+                        lookahead.lexpos = sym.lexpos
+                    #--! TRACKING
+                    statestack.pop()
+                    state = statestack[-1]
+
+                continue
+
+            # Call an error function here
+            raise RuntimeError('yacc: internal parser error!!!\n')
+
+        #--! parseopt-end
+
+    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    # parseopt_notrack().
+    #
+    # Optimized version of parseopt() with line number tracking removed.
+    # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated
+    # by the ply/ygen.py script. Make changes to the parsedebug() method instead.
+    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+    def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        #--! parseopt-notrack-start
+        lookahead = None                         # Current lookahead symbol
+        lookaheadstack = []                      # Stack of lookahead symbols
+        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
+        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
+        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
+        defaulted_states = self.defaulted_states # Local reference to defaulted states
+        pslice  = YaccProduction(None)           # Production object passed to grammar rules
+        errorcount = 0                           # Used during error recovery
+
+
+        # If no lexer was given, we will try to use the lex module
+        if not lexer:
+            from . import lex
+            lexer = lex.lexer
+
+        # Set up the lexer and parser objects on pslice
+        pslice.lexer = lexer
+        pslice.parser = self
+
+        # If input was supplied, pass to lexer
+        if input is not None:
+            lexer.input(input)
+
+        if tokenfunc is None:
+            # Tokenize function
+            get_token = lexer.token
+        else:
+            get_token = tokenfunc
+
+        # Set the parser() token method (sometimes used in error recovery)
+        self.token = get_token
+
+        # Set up the state and symbol stacks
+
+        statestack = []                # Stack of parsing states
+        self.statestack = statestack
+        symstack   = []                # Stack of grammar symbols
+        self.symstack = symstack
+
+        pslice.stack = symstack         # Put in the production
+        errtoken   = None               # Err token
+
+        # The start state is assumed to be (0,$end)
+
+        statestack.append(0)
+        sym = YaccSymbol()
+        sym.type = '$end'
+        symstack.append(sym)
+        state = 0
+        while True:
+            # Get the next symbol on the input.  If a lookahead symbol
+            # is already set, we just use that. Otherwise, we'll pull
+            # the next token off of the lookaheadstack or from the lexer
+
+
+            if state not in defaulted_states:
+                if not lookahead:
+                    if not lookaheadstack:
+                        lookahead = get_token()     # Get the next token
+                    else:
+                        lookahead = lookaheadstack.pop()
+                    if not lookahead:
+                        lookahead = YaccSymbol()
+                        lookahead.type = '$end'
+
+                # Check the action table
+                ltype = lookahead.type
+                t = actions[state].get(ltype)
+            else:
+                t = defaulted_states[state]
+
+
+            if t is not None:
+                if t > 0:
+                    # shift a symbol on the stack
+                    statestack.append(t)
+                    state = t
+
+
+                    symstack.append(lookahead)
+                    lookahead = None
+
+                    # Decrease error count on successful shift
+                    if errorcount:
+                        errorcount -= 1
+                    continue
+
+                if t < 0:
+                    # reduce a symbol on the stack, emit a production
+                    p = prod[-t]
+                    pname = p.name
+                    plen  = p.len
+
+                    # Get production function
+                    sym = YaccSymbol()
+                    sym.type = pname       # Production name
+                    sym.value = None
+
+
+                    if plen:
+                        targ = symstack[-plen-1:]
+                        targ[0] = sym
+
+
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                        # The code enclosed in this section is duplicated
+                        # below as a performance optimization.  Make sure
+                        # changes get made in both locations.
+
+                        pslice.slice = targ
+
+                        try:
+                            # Call the grammar rule with our special slice object
+                            del symstack[-plen:]
+                            self.state = state
+                            p.callable(pslice)
+                            del statestack[-plen:]
+                            symstack.append(sym)
+                            state = goto[statestack[-1]][pname]
+                            statestack.append(state)
+                        except SyntaxError:
+                            # If an error was set. Enter error recovery state
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
+                            statestack.pop()                    # Pop back one state (before the reduce)
+                            state = statestack[-1]
+                            sym.type = 'error'
+                            sym.value = 'error'
+                            lookahead = sym
+                            errorcount = error_count
+                            self.errorok = False
+
+                        continue
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+                    else:
+
+
+                        targ = [sym]
+
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                        # The code enclosed in this section is duplicated
+                        # above as a performance optimization.  Make sure
+                        # changes get made in both locations.
+
+                        pslice.slice = targ
+
+                        try:
+                            # Call the grammar rule with our special slice object
+                            self.state = state
+                            p.callable(pslice)
+                            symstack.append(sym)
+                            state = goto[statestack[-1]][pname]
+                            statestack.append(state)
+                        except SyntaxError:
+                            # If an error was set. Enter error recovery state
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            statestack.pop()                    # Pop back one state (before the reduce)
+                            state = statestack[-1]
+                            sym.type = 'error'
+                            sym.value = 'error'
+                            lookahead = sym
+                            errorcount = error_count
+                            self.errorok = False
+
+                        continue
+                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+                if t == 0:
+                    n = symstack[-1]
+                    result = getattr(n, 'value', None)
+                    return result
+
+            if t is None:
+
+
+                # We have some kind of parsing error here.  To handle
+                # this, we are going to push the current token onto
+                # the tokenstack and replace it with an 'error' token.
+                # If there are any synchronization rules, they may
+                # catch it.
+                #
+                # In addition to pushing the error token, we call call
+                # the user defined p_error() function if this is the
+                # first syntax error.  This function is only called if
+                # errorcount == 0.
+                if errorcount == 0 or self.errorok:
+                    errorcount = error_count
+                    self.errorok = False
+                    errtoken = lookahead
+                    if errtoken.type == '$end':
+                        errtoken = None               # End of file!
+                    if self.errorfunc:
+                        if errtoken and not hasattr(errtoken, 'lexer'):
+                            errtoken.lexer = lexer
+                        self.state = state
+                        tok = call_errorfunc(self.errorfunc, errtoken, self)
+                        if self.errorok:
+                            # User must have done some kind of panic
+                            # mode recovery on their own.  The
+                            # returned token is the next lookahead
+                            lookahead = tok
+                            errtoken = None
+                            continue
+                    else:
+                        if errtoken:
+                            if hasattr(errtoken, 'lineno'):
+                                lineno = lookahead.lineno
+                            else:
+                                lineno = 0
+                            if lineno:
+                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
+                            else:
+                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
+                        else:
+                            sys.stderr.write('yacc: Parse error in input. EOF\n')
+                            return
+
+                else:
+                    errorcount = error_count
+
+                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
+                # entire parse has been rolled back and we're completely hosed.   The token is
+                # discarded and we just keep going.
+
+                if len(statestack) <= 1 and lookahead.type != '$end':
+                    lookahead = None
+                    errtoken = None
+                    state = 0
+                    # Nuke the pushback stack
+                    del lookaheadstack[:]
+                    continue
+
+                # case 2: the statestack has a couple of entries on it, but we're
+                # at the end of the file. nuke the top entry and generate an error token
+
+                # Start nuking entries on the stack
+                if lookahead.type == '$end':
+                    # Whoa. We're really hosed here. Bail out
+                    return
+
+                if lookahead.type != 'error':
+                    sym = symstack[-1]
+                    if sym.type == 'error':
+                        # Hmmm. Error is on top of stack, we'll just nuke input
+                        # symbol and continue
+                        lookahead = None
+                        continue
+
+                    # Create the error symbol for the first time and make it the new lookahead symbol
+                    t = YaccSymbol()
+                    t.type = 'error'
+
+                    if hasattr(lookahead, 'lineno'):
+                        t.lineno = t.endlineno = lookahead.lineno
+                    if hasattr(lookahead, 'lexpos'):
+                        t.lexpos = t.endlexpos = lookahead.lexpos
+                    t.value = lookahead
+                    lookaheadstack.append(lookahead)
+                    lookahead = t
+                else:
+                    sym = symstack.pop()
+                    statestack.pop()
+                    state = statestack[-1]
+
+                continue
+
+            # Call an error function here
+            raise RuntimeError('yacc: internal parser error!!!\n')
+
+        #--! parseopt-notrack-end
+
+# -----------------------------------------------------------------------------
+#                          === Grammar Representation ===
+#
+# The following functions, classes, and variables are used to represent and
+# manipulate the rules that make up a grammar.
+# -----------------------------------------------------------------------------
+
+# regex matching identifiers
+_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
+
+# -----------------------------------------------------------------------------
+# class Production:
+#
+# This class stores the raw information about a single production or grammar rule.
+# A grammar rule refers to a specification such as this:
+#
+#       expr : expr PLUS term
+#
+# Here are the basic attributes defined on all productions
+#
+#       name     - Name of the production.  For example 'expr'
+#       prod     - A list of symbols on the right side ['expr','PLUS','term']
+#       prec     - Production precedence level
+#       number   - Production number.
+#       func     - Function that executes on reduce
+#       file     - File where production function is defined
+#       lineno   - Line number where production function is defined
+#
+# The following attributes are defined or optional.
+#
+#       len       - Length of the production (number of symbols on right hand side)
+#       usyms     - Set of unique symbols found in the production
+# -----------------------------------------------------------------------------
+
+class Production(object):
+    reduced = 0
+    def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0):
+        self.name     = name
+        self.prod     = tuple(prod)
+        self.number   = number
+        self.func     = func
+        self.callable = None
+        self.file     = file
+        self.line     = line
+        self.prec     = precedence
+
+        # Internal settings used during table construction
+
+        self.len  = len(self.prod)   # Length of the production
+
+        # Create a list of unique production symbols used in the production
+        self.usyms = []
+        for s in self.prod:
+            if s not in self.usyms:
+                self.usyms.append(s)
+
+        # List of all LR items for the production
+        self.lr_items = []
+        self.lr_next = None
+
+        # Create a string representation
+        if self.prod:
+            self.str = '%s -> %s' % (self.name, ' '.join(self.prod))
+        else:
+            self.str = '%s -> <empty>' % self.name
+
+    def __str__(self):
+        return self.str
+
+    def __repr__(self):
+        return 'Production(' + str(self) + ')'
+
+    def __len__(self):
+        return len(self.prod)
+
+    def __nonzero__(self):
+        return 1
+
+    def __getitem__(self, index):
+        return self.prod[index]
+
+    # Return the nth lr_item from the production (or None if at the end)
+    def lr_item(self, n):
+        if n > len(self.prod):
+            return None
+        p = LRItem(self, n)
+        # Precompute the list of productions immediately following.
+        try:
+            p.lr_after = Prodnames[p.prod[n+1]]
+        except (IndexError, KeyError):
+            p.lr_after = []
+        try:
+            p.lr_before = p.prod[n-1]
+        except IndexError:
+            p.lr_before = None
+        return p
+
+    # Bind the production function name to a callable
+    def bind(self, pdict):
+        if self.func:
+            self.callable = pdict[self.func]
+
+# This class serves as a minimal standin for Production objects when
+# reading table data from files.   It only contains information
+# actually used by the LR parsing engine, plus some additional
+# debugging information.
+class MiniProduction(object):
+    def __init__(self, str, name, len, func, file, line):
+        self.name     = name
+        self.len      = len
+        self.func     = func
+        self.callable = None
+        self.file     = file
+        self.line     = line
+        self.str      = str
+
+    def __str__(self):
+        return self.str
+
+    def __repr__(self):
+        return 'MiniProduction(%s)' % self.str
+
+    # Bind the production function name to a callable
+    def bind(self, pdict):
+        if self.func:
+            self.callable = pdict[self.func]
+
+
+# -----------------------------------------------------------------------------
+# class LRItem
+#
+# This class represents a specific stage of parsing a production rule.  For
+# example:
+#
+#       expr : expr . PLUS term
+#
+# In the above, the "." represents the current location of the parse.  Here
+# basic attributes:
+#
+#       name       - Name of the production.  For example 'expr'
+#       prod       - A list of symbols on the right side ['expr','.', 'PLUS','term']
+#       number     - Production number.
+#
+#       lr_next      Next LR item. Example, if we are ' expr -> expr . PLUS term'
+#                    then lr_next refers to 'expr -> expr PLUS . term'
+#       lr_index   - LR item index (location of the ".") in the prod list.
+#       lookaheads - LALR lookahead symbols for this item
+#       len        - Length of the production (number of symbols on right hand side)
+#       lr_after    - List of all productions that immediately follow
+#       lr_before   - Grammar symbol immediately before
+# -----------------------------------------------------------------------------
+
+class LRItem(object):
+    def __init__(self, p, n):
+        self.name       = p.name
+        self.prod       = list(p.prod)
+        self.number     = p.number
+        self.lr_index   = n
+        self.lookaheads = {}
+        self.prod.insert(n, '.')
+        self.prod       = tuple(self.prod)
+        self.len        = len(self.prod)
+        self.usyms      = p.usyms
+
+    def __str__(self):
+        if self.prod:
+            s = '%s -> %s' % (self.name, ' '.join(self.prod))
+        else:
+            s = '%s -> <empty>' % self.name
+        return s
+
+    def __repr__(self):
+        return 'LRItem(' + str(self) + ')'
+
+# -----------------------------------------------------------------------------
+# rightmost_terminal()
+#
+# Return the rightmost terminal from a list of symbols.  Used in add_production()
+# -----------------------------------------------------------------------------
+def rightmost_terminal(symbols, terminals):
+    i = len(symbols) - 1
+    while i >= 0:
+        if symbols[i] in terminals:
+            return symbols[i]
+        i -= 1
+    return None
+
+# -----------------------------------------------------------------------------
+#                           === GRAMMAR CLASS ===
+#
+# The following class represents the contents of the specified grammar along
+# with various computed properties such as first sets, follow sets, LR items, etc.
+# This data is used for critical parts of the table generation process later.
+# -----------------------------------------------------------------------------
+
+class GrammarError(YaccError):
+    pass
+
+class Grammar(object):
+    def __init__(self, terminals):
+        self.Productions  = [None]  # A list of all of the productions.  The first
+                                    # entry is always reserved for the purpose of
+                                    # building an augmented grammar
+
+        self.Prodnames    = {}      # A dictionary mapping the names of nonterminals to a list of all
+                                    # productions of that nonterminal.
+
+        self.Prodmap      = {}      # A dictionary that is only used to detect duplicate
+                                    # productions.
+
+        self.Terminals    = {}      # A dictionary mapping the names of terminal symbols to a
+                                    # list of the rules where they are used.
+
+        for term in terminals:
+            self.Terminals[term] = []
+
+        self.Terminals['error'] = []
+
+        self.Nonterminals = {}      # A dictionary mapping names of nonterminals to a list
+                                    # of rule numbers where they are used.
+
+        self.First        = {}      # A dictionary of precomputed FIRST(x) symbols
+
+        self.Follow       = {}      # A dictionary of precomputed FOLLOW(x) symbols
+
+        self.Precedence   = {}      # Precedence rules for each terminal. Contains tuples of the
+                                    # form ('right',level) or ('nonassoc', level) or ('left',level)
+
+        self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer.
+                                    # This is only used to provide error checking and to generate
+                                    # a warning about unused precedence rules.
+
+        self.Start = None           # Starting symbol for the grammar
+
+
+    def __len__(self):
+        return len(self.Productions)
+
+    def __getitem__(self, index):
+        return self.Productions[index]
+
+    # -----------------------------------------------------------------------------
+    # set_precedence()
+    #
+    # Sets the precedence for a given terminal. assoc is the associativity such as
+    # 'left','right', or 'nonassoc'.  level is a numeric level.
+    #
+    # -----------------------------------------------------------------------------
+
+    def set_precedence(self, term, assoc, level):
+        assert self.Productions == [None], 'Must call set_precedence() before add_production()'
+        if term in self.Precedence:
+            raise GrammarError('Precedence already specified for terminal %r' % term)
+        if assoc not in ['left', 'right', 'nonassoc']:
+            raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
+        self.Precedence[term] = (assoc, level)
+
+    # -----------------------------------------------------------------------------
+    # add_production()
+    #
+    # Given an action function, this function assembles a production rule and
+    # computes its precedence level.
+    #
+    # The production rule is supplied as a list of symbols.   For example,
+    # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and
+    # symbols ['expr','PLUS','term'].
+    #
+    # Precedence is determined by the precedence of the right-most non-terminal
+    # or the precedence of a terminal specified by %prec.
+    #
+    # A variety of error checks are performed to make sure production symbols
+    # are valid and that %prec is used correctly.
+    # -----------------------------------------------------------------------------
+
+    def add_production(self, prodname, syms, func=None, file='', line=0):
+
+        if prodname in self.Terminals:
+            raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname))
+        if prodname == 'error':
+            raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname))
+        if not _is_identifier.match(prodname):
+            raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname))
+
+        # Look for literal tokens
+        for n, s in enumerate(syms):
+            if s[0] in "'\"":
+                try:
+                    c = eval(s)
+                    if (len(c) > 1):
+                        raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
+                                           (file, line, s, prodname))
+                    if c not in self.Terminals:
+                        self.Terminals[c] = []
+                    syms[n] = c
+                    continue
+                except SyntaxError:
+                    pass
+            if not _is_identifier.match(s) and s != '%prec':
+                raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname))
+
+        # Determine the precedence level
+        if '%prec' in syms:
+            if syms[-1] == '%prec':
+                raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line))
+            if syms[-2] != '%prec':
+                raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' %
+                                   (file, line))
+            precname = syms[-1]
+            prodprec = self.Precedence.get(precname)
+            if not prodprec:
+                raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname))
+            else:
+                self.UsedPrecedence.add(precname)
+            del syms[-2:]     # Drop %prec from the rule
+        else:
+            # If no %prec, precedence is determined by the rightmost terminal symbol
+            precname = rightmost_terminal(syms, self.Terminals)
+            prodprec = self.Precedence.get(precname, ('right', 0))
+
+        # See if the rule is already in the rulemap
+        map = '%s -> %s' % (prodname, syms)
+        if map in self.Prodmap:
+            m = self.Prodmap[map]
+            raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) +
+                               'Previous definition at %s:%d' % (m.file, m.line))
+
+        # From this point on, everything is valid.  Create a new Production instance
+        pnumber  = len(self.Productions)
+        if prodname not in self.Nonterminals:
+            self.Nonterminals[prodname] = []
+
+        # Add the production number to Terminals and Nonterminals
+        for t in syms:
+            if t in self.Terminals:
+                self.Terminals[t].append(pnumber)
+            else:
+                if t not in self.Nonterminals:
+                    self.Nonterminals[t] = []
+                self.Nonterminals[t].append(pnumber)
+
+        # Create a production and add it to the list of productions
+        p = Production(pnumber, prodname, syms, prodprec, func, file, line)
+        self.Productions.append(p)
+        self.Prodmap[map] = p
+
+        # Add to the global productions list
+        try:
+            self.Prodnames[prodname].append(p)
+        except KeyError:
+            self.Prodnames[prodname] = [p]
+
+    # -----------------------------------------------------------------------------
+    # set_start()
+    #
+    # Sets the starting symbol and creates the augmented grammar.  Production
+    # rule 0 is S' -> start where start is the start symbol.
+    # -----------------------------------------------------------------------------
+
+    def set_start(self, start=None):
+        if not start:
+            start = self.Productions[1].name
+        if start not in self.Nonterminals:
+            raise GrammarError('start symbol %s undefined' % start)
+        self.Productions[0] = Production(0, "S'", [start])
+        self.Nonterminals[start].append(0)
+        self.Start = start
+
+    # -----------------------------------------------------------------------------
+    # find_unreachable()
+    #
+    # Find all of the nonterminal symbols that can't be reached from the starting
+    # symbol.  Returns a list of nonterminals that can't be reached.
+    # -----------------------------------------------------------------------------
+
+    def find_unreachable(self):
+
+        # Mark all symbols that are reachable from a symbol s
+        def mark_reachable_from(s):
+            if s in reachable:
+                return
+            reachable.add(s)
+            for p in self.Prodnames.get(s, []):
+                for r in p.prod:
+                    mark_reachable_from(r)
+
+        reachable = set()
+        mark_reachable_from(self.Productions[0].prod[0])
+        return [s for s in self.Nonterminals if s not in reachable]
+
+    # -----------------------------------------------------------------------------
+    # infinite_cycles()
+    #
+    # This function looks at the various parsing rules and tries to detect
+    # infinite recursion cycles (grammar rules where there is no possible way
+    # to derive a string of only terminals).
+    # -----------------------------------------------------------------------------
+
+    def infinite_cycles(self):
+        terminates = {}
+
+        # Terminals:
+        for t in self.Terminals:
+            terminates[t] = True
+
+        terminates['$end'] = True
+
+        # Nonterminals:
+
+        # Initialize to false:
+        for n in self.Nonterminals:
+            terminates[n] = False
+
+        # Then propagate termination until no change:
+        while True:
+            some_change = False
+            for (n, pl) in self.Prodnames.items():
+                # Nonterminal n terminates iff any of its productions terminates.
+                for p in pl:
+                    # Production p terminates iff all of its rhs symbols terminate.
+                    for s in p.prod:
+                        if not terminates[s]:
+                            # The symbol s does not terminate,
+                            # so production p does not terminate.
+                            p_terminates = False
+                            break
+                    else:
+                        # didn't break from the loop,
+                        # so every symbol s terminates
+                        # so production p terminates.
+                        p_terminates = True
+
+                    if p_terminates:
+                        # symbol n terminates!
+                        if not terminates[n]:
+                            terminates[n] = True
+                            some_change = True
+                        # Don't need to consider any more productions for this n.
+                        break
+
+            if not some_change:
+                break
+
+        infinite = []
+        for (s, term) in terminates.items():
+            if not term:
+                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
+                    # s is used-but-not-defined, and we've already warned of that,
+                    # so it would be overkill to say that it's also non-terminating.
+                    pass
+                else:
+                    infinite.append(s)
+
+        return infinite
+
+    # -----------------------------------------------------------------------------
+    # undefined_symbols()
+    #
+    # Find all symbols that were used the grammar, but not defined as tokens or
+    # grammar rules.  Returns a list of tuples (sym, prod) where sym in the symbol
+    # and prod is the production where the symbol was used.
+    # -----------------------------------------------------------------------------
+    def undefined_symbols(self):
+        result = []
+        for p in self.Productions:
+            if not p:
+                continue
+
+            for s in p.prod:
+                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
+                    result.append((s, p))
+        return result
+
+    # -----------------------------------------------------------------------------
+    # unused_terminals()
+    #
+    # Find all terminals that were defined, but not used by the grammar.  Returns
+    # a list of all symbols.
+    # -----------------------------------------------------------------------------
+    def unused_terminals(self):
+        unused_tok = []
+        for s, v in self.Terminals.items():
+            if s != 'error' and not v:
+                unused_tok.append(s)
+
+        return unused_tok
+
+    # ------------------------------------------------------------------------------
+    # unused_rules()
+    #
+    # Find all grammar rules that were defined,  but not used (maybe not reachable)
+    # Returns a list of productions.
+    # ------------------------------------------------------------------------------
+
+    def unused_rules(self):
+        unused_prod = []
+        for s, v in self.Nonterminals.items():
+            if not v:
+                p = self.Prodnames[s][0]
+                unused_prod.append(p)
+        return unused_prod
+
+    # -----------------------------------------------------------------------------
+    # unused_precedence()
+    #
+    # Returns a list of tuples (term,precedence) corresponding to precedence
+    # rules that were never used by the grammar.  term is the name of the terminal
+    # on which precedence was applied and precedence is a string such as 'left' or
+    # 'right' corresponding to the type of precedence.
+    # -----------------------------------------------------------------------------
+
+    def unused_precedence(self):
+        unused = []
+        for termname in self.Precedence:
+            if not (termname in self.Terminals or termname in self.UsedPrecedence):
+                unused.append((termname, self.Precedence[termname][0]))
+
+        return unused
+
+    # -------------------------------------------------------------------------
+    # _first()
+    #
+    # Compute the value of FIRST1(beta) where beta is a tuple of symbols.
+    #
+    # During execution of compute_first1, the result may be incomplete.
+    # Afterward (e.g., when called from compute_follow()), it will be complete.
+    # -------------------------------------------------------------------------
+    def _first(self, beta):
+
+        # We are computing First(x1,x2,x3,...,xn)
+        result = []
+        for x in beta:
+            x_produces_empty = False
+
+            # Add all the non-<empty> symbols of First[x] to the result.
+            for f in self.First[x]:
+                if f == '<empty>':
+                    x_produces_empty = True
+                else:
+                    if f not in result:
+                        result.append(f)
+
+            if x_produces_empty:
+                # We have to consider the next x in beta,
+                # i.e. stay in the loop.
+                pass
+            else:
+                # We don't have to consider any further symbols in beta.
+                break
+        else:
+            # There was no 'break' from the loop,
+            # so x_produces_empty was true for all x in beta,
+            # so beta produces empty as well.
+            result.append('<empty>')
+
+        return result
+
+    # -------------------------------------------------------------------------
+    # compute_first()
+    #
+    # Compute the value of FIRST1(X) for all symbols
+    # -------------------------------------------------------------------------
+    def compute_first(self):
+        if self.First:
+            return self.First
+
+        # Terminals:
+        for t in self.Terminals:
+            self.First[t] = [t]
+
+        self.First['$end'] = ['$end']
+
+        # Nonterminals:
+
+        # Initialize to the empty set:
+        for n in self.Nonterminals:
+            self.First[n] = []
+
+        # Then propagate symbols until no change:
+        while True:
+            some_change = False
+            for n in self.Nonterminals:
+                for p in self.Prodnames[n]:
+                    for f in self._first(p.prod):
+                        if f not in self.First[n]:
+                            self.First[n].append(f)
+                            some_change = True
+            if not some_change:
+                break
+
+        return self.First
+
+    # ---------------------------------------------------------------------
+    # compute_follow()
+    #
+    # Computes all of the follow sets for every non-terminal symbol.  The
+    # follow set is the set of all symbols that might follow a given
+    # non-terminal.  See the Dragon book, 2nd Ed. p. 189.
+    # ---------------------------------------------------------------------
+    def compute_follow(self, start=None):
+        # If already computed, return the result
+        if self.Follow:
+            return self.Follow
+
+        # If first sets not computed yet, do that first.
+        if not self.First:
+            self.compute_first()
+
+        # Add '$end' to the follow list of the start symbol
+        for k in self.Nonterminals:
+            self.Follow[k] = []
+
+        if not start:
+            start = self.Productions[1].name
+
+        self.Follow[start] = ['$end']
+
+        while True:
+            didadd = False
+            for p in self.Productions[1:]:
+                # Here is the production set
+                for i, B in enumerate(p.prod):
+                    if B in self.Nonterminals:
+                        # Okay. We got a non-terminal in a production
+                        fst = self._first(p.prod[i+1:])
+                        hasempty = False
+                        for f in fst:
+                            if f != '<empty>' and f not in self.Follow[B]:
+                                self.Follow[B].append(f)
+                                didadd = True
+                            if f == '<empty>':
+                                hasempty = True
+                        if hasempty or i == (len(p.prod)-1):
+                            # Add elements of follow(a) to follow(b)
+                            for f in self.Follow[p.name]:
+                                if f not in self.Follow[B]:
+                                    self.Follow[B].append(f)
+                                    didadd = True
+            if not didadd:
+                break
+        return self.Follow
+
+
+    # -----------------------------------------------------------------------------
+    # build_lritems()
+    #
+    # This function walks the list of productions and builds a complete set of the
+    # LR items.  The LR items are stored in two ways:  First, they are uniquely
+    # numbered and placed in the list _lritems.  Second, a linked list of LR items
+    # is built for each production.  For example:
+    #
+    #   E -> E PLUS E
+    #
+    # Creates the list
+    #
+    #  [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ]
+    # -----------------------------------------------------------------------------
+
+    def build_lritems(self):
+        for p in self.Productions:
+            lastlri = p
+            i = 0
+            lr_items = []
+            while True:
+                if i > len(p):
+                    lri = None
+                else:
+                    lri = LRItem(p, i)
+                    # Precompute the list of productions immediately following
+                    try:
+                        lri.lr_after = self.Prodnames[lri.prod[i+1]]
+                    except (IndexError, KeyError):
+                        lri.lr_after = []
+                    try:
+                        lri.lr_before = lri.prod[i-1]
+                    except IndexError:
+                        lri.lr_before = None
+
+                lastlri.lr_next = lri
+                if not lri:
+                    break
+                lr_items.append(lri)
+                lastlri = lri
+                i += 1
+            p.lr_items = lr_items
+
+# -----------------------------------------------------------------------------
+#                            == Class LRTable ==
+#
+# This basic class represents a basic table of LR parsing information.
+# Methods for generating the tables are not defined here.  They are defined
+# in the derived class LRGeneratedTable.
+# -----------------------------------------------------------------------------
+
+class VersionError(YaccError):
+    pass
+
+class LRTable(object):
+    def __init__(self):
+        self.lr_action = None
+        self.lr_goto = None
+        self.lr_productions = None
+        self.lr_method = None
+
+    def read_table(self, module):
+        if isinstance(module, types.ModuleType):
+            parsetab = module
+        else:
+            exec('import %s' % module)
+            parsetab = sys.modules[module]
+
+        if parsetab._tabversion != __tabversion__:
+            raise VersionError('yacc table file version is out of date')
+
+        self.lr_action = parsetab._lr_action
+        self.lr_goto = parsetab._lr_goto
+
+        self.lr_productions = []
+        for p in parsetab._lr_productions:
+            self.lr_productions.append(MiniProduction(*p))
+
+        self.lr_method = parsetab._lr_method
+        return parsetab._lr_signature
+
+    def read_pickle(self, filename):
+        try:
+            import cPickle as pickle
+        except ImportError:
+            import pickle
+
+        if not os.path.exists(filename):
+          raise ImportError
+
+        in_f = open(filename, 'rb')
+
+        tabversion = pickle.load(in_f)
+        if tabversion != __tabversion__:
+            raise VersionError('yacc table file version is out of date')
+        self.lr_method = pickle.load(in_f)
+        signature      = pickle.load(in_f)
+        self.lr_action = pickle.load(in_f)
+        self.lr_goto   = pickle.load(in_f)
+        productions    = pickle.load(in_f)
+
+        self.lr_productions = []
+        for p in productions:
+            self.lr_productions.append(MiniProduction(*p))
+
+        in_f.close()
+        return signature
+
+    # Bind all production function names to callable objects in pdict
+    def bind_callables(self, pdict):
+        for p in self.lr_productions:
+            p.bind(pdict)
+
+
+# -----------------------------------------------------------------------------
+#                           === LR Generator ===
+#
+# The following classes and functions are used to generate LR parsing tables on
+# a grammar.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# digraph()
+# traverse()
+#
+# The following two functions are used to compute set valued functions
+# of the form:
+#
+#     F(x) = F'(x) U U{F(y) | x R y}
+#
+# This is used to compute the values of Read() sets as well as FOLLOW sets
+# in LALR(1) generation.
+#
+# Inputs:  X    - An input set
+#          R    - A relation
+#          FP   - Set-valued function
+# ------------------------------------------------------------------------------
+
+def digraph(X, R, FP):
+    N = {}
+    for x in X:
+        N[x] = 0
+    stack = []
+    F = {}
+    for x in X:
+        if N[x] == 0:
+            traverse(x, N, stack, F, X, R, FP)
+    return F
+
+def traverse(x, N, stack, F, X, R, FP):
+    stack.append(x)
+    d = len(stack)
+    N[x] = d
+    F[x] = FP(x)             # F(X) <- F'(x)
+
+    rel = R(x)               # Get y's related to x
+    for y in rel:
+        if N[y] == 0:
+            traverse(y, N, stack, F, X, R, FP)
+        N[x] = min(N[x], N[y])
+        for a in F.get(y, []):
+            if a not in F[x]:
+                F[x].append(a)
+    if N[x] == d:
+        N[stack[-1]] = MAXINT
+        F[stack[-1]] = F[x]
+        element = stack.pop()
+        while element != x:
+            N[stack[-1]] = MAXINT
+            F[stack[-1]] = F[x]
+            element = stack.pop()
+
+class LALRError(YaccError):
+    pass
+
+# -----------------------------------------------------------------------------
+#                             == LRGeneratedTable ==
+#
+# This class implements the LR table generation algorithm.  There are no
+# public methods except for write()
+# -----------------------------------------------------------------------------
+
+class LRGeneratedTable(LRTable):
+    def __init__(self, grammar, method='LALR', log=None):
+        if method not in ['SLR', 'LALR']:
+            raise LALRError('Unsupported method %s' % method)
+
+        self.grammar = grammar
+        self.lr_method = method
+
+        # Set up the logger
+        if not log:
+            log = NullLogger()
+        self.log = log
+
+        # Internal attributes
+        self.lr_action     = {}        # Action table
+        self.lr_goto       = {}        # Goto table
+        self.lr_productions  = grammar.Productions    # Copy of grammar Production array
+        self.lr_goto_cache = {}        # Cache of computed gotos
+        self.lr0_cidhash   = {}        # Cache of closures
+
+        self._add_count    = 0         # Internal counter used to detect cycles
+
+        # Diagonistic information filled in by the table generator
+        self.sr_conflict   = 0
+        self.rr_conflict   = 0
+        self.conflicts     = []        # List of conflicts
+
+        self.sr_conflicts  = []
+        self.rr_conflicts  = []
+
+        # Build the tables
+        self.grammar.build_lritems()
+        self.grammar.compute_first()
+        self.grammar.compute_follow()
+        self.lr_parse_table()
+
+    # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
+
+    def lr0_closure(self, I):
+        self._add_count += 1
+
+        # Add everything in I to J
+        J = I[:]
+        didadd = True
+        while didadd:
+            didadd = False
+            for j in J:
+                for x in j.lr_after:
+                    if getattr(x, 'lr0_added', 0) == self._add_count:
+                        continue
+                    # Add B --> .G to J
+                    J.append(x.lr_next)
+                    x.lr0_added = self._add_count
+                    didadd = True
+
+        return J
+
+    # Compute the LR(0) goto function goto(I,X) where I is a set
+    # of LR(0) items and X is a grammar symbol.   This function is written
+    # in a way that guarantees uniqueness of the generated goto sets
+    # (i.e. the same goto set will never be returned as two different Python
+    # objects).  With uniqueness, we can later do fast set comparisons using
+    # id(obj) instead of element-wise comparison.
+
+    def lr0_goto(self, I, x):
+        # First we look for a previously cached entry
+        g = self.lr_goto_cache.get((id(I), x))
+        if g:
+            return g
+
+        # Now we generate the goto set in a way that guarantees uniqueness
+        # of the result
+
+        s = self.lr_goto_cache.get(x)
+        if not s:
+            s = {}
+            self.lr_goto_cache[x] = s
+
+        gs = []
+        for p in I:
+            n = p.lr_next
+            if n and n.lr_before == x:
+                s1 = s.get(id(n))
+                if not s1:
+                    s1 = {}
+                    s[id(n)] = s1
+                gs.append(n)
+                s = s1
+        g = s.get('$end')
+        if not g:
+            if gs:
+                g = self.lr0_closure(gs)
+                s['$end'] = g
+            else:
+                s['$end'] = gs
+        self.lr_goto_cache[(id(I), x)] = g
+        return g
+
+    # Compute the LR(0) sets of item function
+    def lr0_items(self):
+        C = [self.lr0_closure([self.grammar.Productions[0].lr_next])]
+        i = 0
+        for I in C:
+            self.lr0_cidhash[id(I)] = i
+            i += 1
+
+        # Loop over the items in C and each grammar symbols
+        i = 0
+        while i < len(C):
+            I = C[i]
+            i += 1
+
+            # Collect all of the symbols that could possibly be in the goto(I,X) sets
+            asyms = {}
+            for ii in I:
+                for s in ii.usyms:
+                    asyms[s] = None
+
+            for x in asyms:
+                g = self.lr0_goto(I, x)
+                if not g or id(g) in self.lr0_cidhash:
+                    continue
+                self.lr0_cidhash[id(g)] = len(C)
+                C.append(g)
+
+        return C
+
+    # -----------------------------------------------------------------------------
+    #                       ==== LALR(1) Parsing ====
+    #
+    # LALR(1) parsing is almost exactly the same as SLR except that instead of
+    # relying upon Follow() sets when performing reductions, a more selective
+    # lookahead set that incorporates the state of the LR(0) machine is utilized.
+    # Thus, we mainly just have to focus on calculating the lookahead sets.
+    #
+    # The method used here is due to DeRemer and Pennelo (1982).
+    #
+    # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1)
+    #     Lookahead Sets", ACM Transactions on Programming Languages and Systems,
+    #     Vol. 4, No. 4, Oct. 1982, pp. 615-649
+    #
+    # Further details can also be found in:
+    #
+    #  J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing",
+    #      McGraw-Hill Book Company, (1985).
+    #
+    # -----------------------------------------------------------------------------
+
+    # -----------------------------------------------------------------------------
+    # compute_nullable_nonterminals()
+    #
+    # Creates a dictionary containing all of the non-terminals that might produce
+    # an empty production.
+    # -----------------------------------------------------------------------------
+
+    def compute_nullable_nonterminals(self):
+        nullable = set()
+        num_nullable = 0
+        while True:
+            for p in self.grammar.Productions[1:]:
+                if p.len == 0:
+                    nullable.add(p.name)
+                    continue
+                for t in p.prod:
+                    if t not in nullable:
+                        break
+                else:
+                    nullable.add(p.name)
+            if len(nullable) == num_nullable:
+                break
+            num_nullable = len(nullable)
+        return nullable
+
+    # -----------------------------------------------------------------------------
+    # find_nonterminal_trans(C)
+    #
+    # Given a set of LR(0) items, this functions finds all of the non-terminal
+    # transitions.    These are transitions in which a dot appears immediately before
+    # a non-terminal.   Returns a list of tuples of the form (state,N) where state
+    # is the state number and N is the nonterminal symbol.
+    #
+    # The input C is the set of LR(0) items.
+    # -----------------------------------------------------------------------------
+
+    def find_nonterminal_transitions(self, C):
+        trans = []
+        for stateno, state in enumerate(C):
+            for p in state:
+                if p.lr_index < p.len - 1:
+                    t = (stateno, p.prod[p.lr_index+1])
+                    if t[1] in self.grammar.Nonterminals:
+                        if t not in trans:
+                            trans.append(t)
+        return trans
+
+    # -----------------------------------------------------------------------------
+    # dr_relation()
+    #
+    # Computes the DR(p,A) relationships for non-terminal transitions.  The input
+    # is a tuple (state,N) where state is a number and N is a nonterminal symbol.
+    #
+    # Returns a list of terminals.
+    # -----------------------------------------------------------------------------
+
+    def dr_relation(self, C, trans, nullable):
+        dr_set = {}
+        state, N = trans
+        terms = []
+
+        g = self.lr0_goto(C[state], N)
+        for p in g:
+            if p.lr_index < p.len - 1:
+                a = p.prod[p.lr_index+1]
+                if a in self.grammar.Terminals:
+                    if a not in terms:
+                        terms.append(a)
+
+        # This extra bit is to handle the start state
+        if state == 0 and N == self.grammar.Productions[0].prod[0]:
+            terms.append('$end')
+
+        return terms
+
+    # -----------------------------------------------------------------------------
+    # reads_relation()
+    #
+    # Computes the READS() relation (p,A) READS (t,C).
+    # -----------------------------------------------------------------------------
+
+    def reads_relation(self, C, trans, empty):
+        # Look for empty transitions
+        rel = []
+        state, N = trans
+
+        g = self.lr0_goto(C[state], N)
+        j = self.lr0_cidhash.get(id(g), -1)
+        for p in g:
+            if p.lr_index < p.len - 1:
+                a = p.prod[p.lr_index + 1]
+                if a in empty:
+                    rel.append((j, a))
+
+        return rel
+
+    # -----------------------------------------------------------------------------
+    # compute_lookback_includes()
+    #
+    # Determines the lookback and includes relations
+    #
+    # LOOKBACK:
+    #
+    # This relation is determined by running the LR(0) state machine forward.
+    # For example, starting with a production "N : . A B C", we run it forward
+    # to obtain "N : A B C ."   We then build a relationship between this final
+    # state and the starting state.   These relationships are stored in a dictionary
+    # lookdict.
+    #
+    # INCLUDES:
+    #
+    # Computes the INCLUDE() relation (p,A) INCLUDES (p',B).
+    #
+    # This relation is used to determine non-terminal transitions that occur
+    # inside of other non-terminal transition states.   (p,A) INCLUDES (p', B)
+    # if the following holds:
+    #
+    #       B -> LAT, where T -> epsilon and p' -L-> p
+    #
+    # L is essentially a prefix (which may be empty), T is a suffix that must be
+    # able to derive an empty string.  State p' must lead to state p with the string L.
+    #
+    # -----------------------------------------------------------------------------
+
+    def compute_lookback_includes(self, C, trans, nullable):
+        lookdict = {}          # Dictionary of lookback relations
+        includedict = {}       # Dictionary of include relations
+
+        # Make a dictionary of non-terminal transitions
+        dtrans = {}
+        for t in trans:
+            dtrans[t] = 1
+
+        # Loop over all transitions and compute lookbacks and includes
+        for state, N in trans:
+            lookb = []
+            includes = []
+            for p in C[state]:
+                if p.name != N:
+                    continue
+
+                # Okay, we have a name match.  We now follow the production all the way
+                # through the state machine until we get the . on the right hand side
+
+                lr_index = p.lr_index
+                j = state
+                while lr_index < p.len - 1:
+                    lr_index = lr_index + 1
+                    t = p.prod[lr_index]
+
+                    # Check to see if this symbol and state are a non-terminal transition
+                    if (j, t) in dtrans:
+                        # Yes.  Okay, there is some chance that this is an includes relation
+                        # the only way to know for certain is whether the rest of the
+                        # production derives empty
+
+                        li = lr_index + 1
+                        while li < p.len:
+                            if p.prod[li] in self.grammar.Terminals:
+                                break      # No forget it
+                            if p.prod[li] not in nullable:
+                                break
+                            li = li + 1
+                        else:
+                            # Appears to be a relation between (j,t) and (state,N)
+                            includes.append((j, t))
+
+                    g = self.lr0_goto(C[j], t)               # Go to next set
+                    j = self.lr0_cidhash.get(id(g), -1)      # Go to next state
+
+                # When we get here, j is the final state, now we have to locate the production
+                for r in C[j]:
+                    if r.name != p.name:
+                        continue
+                    if r.len != p.len:
+                        continue
+                    i = 0
+                    # This look is comparing a production ". A B C" with "A B C ."
+                    while i < r.lr_index:
+                        if r.prod[i] != p.prod[i+1]:
+                            break
+                        i = i + 1
+                    else:
+                        lookb.append((j, r))
+            for i in includes:
+                if i not in includedict:
+                    includedict[i] = []
+                includedict[i].append((state, N))
+            lookdict[(state, N)] = lookb
+
+        return lookdict, includedict
+
+    # -----------------------------------------------------------------------------
+    # compute_read_sets()
+    #
+    # Given a set of LR(0) items, this function computes the read sets.
+    #
+    # Inputs:  C        =  Set of LR(0) items
+    #          ntrans   = Set of nonterminal transitions
+    #          nullable = Set of empty transitions
+    #
+    # Returns a set containing the read sets
+    # -----------------------------------------------------------------------------
+
+    def compute_read_sets(self, C, ntrans, nullable):
+        FP = lambda x: self.dr_relation(C, x, nullable)
+        R =  lambda x: self.reads_relation(C, x, nullable)
+        F = digraph(ntrans, R, FP)
+        return F
+
+    # -----------------------------------------------------------------------------
+    # compute_follow_sets()
+    #
+    # Given a set of LR(0) items, a set of non-terminal transitions, a readset,
+    # and an include set, this function computes the follow sets
+    #
+    # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)}
+    #
+    # Inputs:
+    #            ntrans     = Set of nonterminal transitions
+    #            readsets   = Readset (previously computed)
+    #            inclsets   = Include sets (previously computed)
+    #
+    # Returns a set containing the follow sets
+    # -----------------------------------------------------------------------------
+
+    def compute_follow_sets(self, ntrans, readsets, inclsets):
+        FP = lambda x: readsets[x]
+        R  = lambda x: inclsets.get(x, [])
+        F = digraph(ntrans, R, FP)
+        return F
+
+    # -----------------------------------------------------------------------------
+    # add_lookaheads()
+    #
+    # Attaches the lookahead symbols to grammar rules.
+    #
+    # Inputs:    lookbacks         -  Set of lookback relations
+    #            followset         -  Computed follow set
+    #
+    # This function directly attaches the lookaheads to productions contained
+    # in the lookbacks set
+    # -----------------------------------------------------------------------------
+
+    def add_lookaheads(self, lookbacks, followset):
+        for trans, lb in lookbacks.items():
+            # Loop over productions in lookback
+            for state, p in lb:
+                if state not in p.lookaheads:
+                    p.lookaheads[state] = []
+                f = followset.get(trans, [])
+                for a in f:
+                    if a not in p.lookaheads[state]:
+                        p.lookaheads[state].append(a)
+
+    # -----------------------------------------------------------------------------
+    # add_lalr_lookaheads()
+    #
+    # This function does all of the work of adding lookahead information for use
+    # with LALR parsing
+    # -----------------------------------------------------------------------------
+
+    def add_lalr_lookaheads(self, C):
+        # Determine all of the nullable nonterminals
+        nullable = self.compute_nullable_nonterminals()
+
+        # Find all non-terminal transitions
+        trans = self.find_nonterminal_transitions(C)
+
+        # Compute read sets
+        readsets = self.compute_read_sets(C, trans, nullable)
+
+        # Compute lookback/includes relations
+        lookd, included = self.compute_lookback_includes(C, trans, nullable)
+
+        # Compute LALR FOLLOW sets
+        followsets = self.compute_follow_sets(trans, readsets, included)
+
+        # Add all of the lookaheads
+        self.add_lookaheads(lookd, followsets)
+
+    # -----------------------------------------------------------------------------
+    # lr_parse_table()
+    #
+    # This function constructs the parse tables for SLR or LALR
+    # -----------------------------------------------------------------------------
+    def lr_parse_table(self):
+        Productions = self.grammar.Productions
+        Precedence  = self.grammar.Precedence
+        goto   = self.lr_goto         # Goto array
+        action = self.lr_action       # Action array
+        log    = self.log             # Logger for output
+
+        actionp = {}                  # Action production array (temporary)
+
+        log.info('Parsing method: %s', self.lr_method)
+
+        # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
+        # This determines the number of states
+
+        C = self.lr0_items()
+
+        if self.lr_method == 'LALR':
+            self.add_lalr_lookaheads(C)
+
+        # Build the parser table, state by state
+        st = 0
+        for I in C:
+            # Loop over each production in I
+            actlist = []              # List of actions
+            st_action  = {}
+            st_actionp = {}
+            st_goto    = {}
+            log.info('')
+            log.info('state %d', st)
+            log.info('')
+            for p in I:
+                log.info('    (%d) %s', p.number, p)
+            log.info('')
+
+            for p in I:
+                    if p.len == p.lr_index + 1:
+                        if p.name == "S'":
+                            # Start symbol. Accept!
+                            st_action['$end'] = 0
+                            st_actionp['$end'] = p
+                        else:
+                            # We are at the end of a production.  Reduce!
+                            if self.lr_method == 'LALR':
+                                laheads = p.lookaheads[st]
+                            else:
+                                laheads = self.grammar.Follow[p.name]
+                            for a in laheads:
+                                actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p)))
+                                r = st_action.get(a)
+                                if r is not None:
+                                    # Whoa. Have a shift/reduce or reduce/reduce conflict
+                                    if r > 0:
+                                        # Need to decide on shift or reduce here
+                                        # By default we favor shifting. Need to add
+                                        # some precedence rules here.
+
+                                        # Shift precedence comes from the token
+                                        sprec, slevel = Precedence.get(a, ('right', 0))
+
+                                        # Reduce precedence comes from rule being reduced (p)
+                                        rprec, rlevel = Productions[p.number].prec
+
+                                        if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
+                                            # We really need to reduce here.
+                                            st_action[a] = -p.number
+                                            st_actionp[a] = p
+                                            if not slevel and not rlevel:
+                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
+                                                self.sr_conflicts.append((st, a, 'reduce'))
+                                            Productions[p.number].reduced += 1
+                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
+                                            st_action[a] = None
+                                        else:
+                                            # Hmmm. Guess we'll keep the shift
+                                            if not rlevel:
+                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
+                                                self.sr_conflicts.append((st, a, 'shift'))
+                                    elif r < 0:
+                                        # Reduce/reduce conflict.   In this case, we favor the rule
+                                        # that was defined first in the grammar file
+                                        oldp = Productions[-r]
+                                        pp = Productions[p.number]
+                                        if oldp.line > pp.line:
+                                            st_action[a] = -p.number
+                                            st_actionp[a] = p
+                                            chosenp, rejectp = pp, oldp
+                                            Productions[p.number].reduced += 1
+                                            Productions[oldp.number].reduced -= 1
+                                        else:
+                                            chosenp, rejectp = oldp, pp
+                                        self.rr_conflicts.append((st, chosenp, rejectp))
+                                        log.info('  ! reduce/reduce conflict for %s resolved using rule %d (%s)',
+                                                 a, st_actionp[a].number, st_actionp[a])
+                                    else:
+                                        raise LALRError('Unknown conflict in state %d' % st)
+                                else:
+                                    st_action[a] = -p.number
+                                    st_actionp[a] = p
+                                    Productions[p.number].reduced += 1
+                    else:
+                        i = p.lr_index
+                        a = p.prod[i+1]       # Get symbol right after the "."
+                        if a in self.grammar.Terminals:
+                            g = self.lr0_goto(I, a)
+                            j = self.lr0_cidhash.get(id(g), -1)
+                            if j >= 0:
+                                # We are in a shift state
+                                actlist.append((a, p, 'shift and go to state %d' % j))
+                                r = st_action.get(a)
+                                if r is not None:
+                                    # Whoa have a shift/reduce or shift/shift conflict
+                                    if r > 0:
+                                        if r != j:
+                                            raise LALRError('Shift/shift conflict in state %d' % st)
+                                    elif r < 0:
+                                        # Do a precedence check.
+                                        #   -  if precedence of reduce rule is higher, we reduce.
+                                        #   -  if precedence of reduce is same and left assoc, we reduce.
+                                        #   -  otherwise we shift
+
+                                        # Shift precedence comes from the token
+                                        sprec, slevel = Precedence.get(a, ('right', 0))
+
+                                        # Reduce precedence comes from the rule that could have been reduced
+                                        rprec, rlevel = Productions[st_actionp[a].number].prec
+
+                                        if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
+                                            # We decide to shift here... highest precedence to shift
+                                            Productions[st_actionp[a].number].reduced -= 1
+                                            st_action[a] = j
+                                            st_actionp[a] = p
+                                            if not rlevel:
+                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
+                                                self.sr_conflicts.append((st, a, 'shift'))
+                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
+                                            st_action[a] = None
+                                        else:
+                                            # Hmmm. Guess we'll keep the reduce
+                                            if not slevel and not rlevel:
+                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
+                                                self.sr_conflicts.append((st, a, 'reduce'))
+
+                                    else:
+                                        raise LALRError('Unknown conflict in state %d' % st)
+                                else:
+                                    st_action[a] = j
+                                    st_actionp[a] = p
+
+            # Print the actions associated with each terminal
+            _actprint = {}
+            for a, p, m in actlist:
+                if a in st_action:
+                    if p is st_actionp[a]:
+                        log.info('    %-15s %s', a, m)
+                        _actprint[(a, m)] = 1
+            log.info('')
+            # Print the actions that were not used. (debugging)
+            not_used = 0
+            for a, p, m in actlist:
+                if a in st_action:
+                    if p is not st_actionp[a]:
+                        if not (a, m) in _actprint:
+                            log.debug('  ! %-15s [ %s ]', a, m)
+                            not_used = 1
+                            _actprint[(a, m)] = 1
+            if not_used:
+                log.debug('')
+
+            # Construct the goto table for this state
+
+            nkeys = {}
+            for ii in I:
+                for s in ii.usyms:
+                    if s in self.grammar.Nonterminals:
+                        nkeys[s] = None
+            for n in nkeys:
+                g = self.lr0_goto(I, n)
+                j = self.lr0_cidhash.get(id(g), -1)
+                if j >= 0:
+                    st_goto[n] = j
+                    log.info('    %-30s shift and go to state %d', n, j)
+
+            action[st] = st_action
+            actionp[st] = st_actionp
+            goto[st] = st_goto
+            st += 1
+
+    # -----------------------------------------------------------------------------
+    # write()
+    #
+    # This function writes the LR parsing tables to a file
+    # -----------------------------------------------------------------------------
+
+    def write_table(self, tabmodule, outputdir='', signature=''):
+        if isinstance(tabmodule, types.ModuleType):
+            raise IOError("Won't overwrite existing tabmodule")
+
+        basemodulename = tabmodule.split('.')[-1]
+        filename = os.path.join(outputdir, basemodulename) + '.py'
+        try:
+            f = open(filename, 'w')
+
+            f.write('''
+# %s
+# This file is automatically generated. Do not edit.
+_tabversion = %r
+
+_lr_method = %r
+
+_lr_signature = %r
+    ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature))
+
+            # Change smaller to 0 to go back to original tables
+            smaller = 1
+
+            # Factor out names to try and make smaller
+            if smaller:
+                items = {}
+
+                for s, nd in self.lr_action.items():
+                    for name, v in nd.items():
+                        i = items.get(name)
+                        if not i:
+                            i = ([], [])
+                            items[name] = i
+                        i[0].append(s)
+                        i[1].append(v)
+
+                f.write('\n_lr_action_items = {')
+                for k, v in items.items():
+                    f.write('%r:([' % k)
+                    for i in v[0]:
+                        f.write('%r,' % i)
+                    f.write('],[')
+                    for i in v[1]:
+                        f.write('%r,' % i)
+
+                    f.write(']),')
+                f.write('}\n')
+
+                f.write('''
+_lr_action = {}
+for _k, _v in _lr_action_items.items():
+   for _x,_y in zip(_v[0],_v[1]):
+      if not _x in _lr_action:  _lr_action[_x] = {}
+      _lr_action[_x][_k] = _y
+del _lr_action_items
+''')
+
+            else:
+                f.write('\n_lr_action = { ')
+                for k, v in self.lr_action.items():
+                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
+                f.write('}\n')
+
+            if smaller:
+                # Factor out names to try and make smaller
+                items = {}
+
+                for s, nd in self.lr_goto.items():
+                    for name, v in nd.items():
+                        i = items.get(name)
+                        if not i:
+                            i = ([], [])
+                            items[name] = i
+                        i[0].append(s)
+                        i[1].append(v)
+
+                f.write('\n_lr_goto_items = {')
+                for k, v in items.items():
+                    f.write('%r:([' % k)
+                    for i in v[0]:
+                        f.write('%r,' % i)
+                    f.write('],[')
+                    for i in v[1]:
+                        f.write('%r,' % i)
+
+                    f.write(']),')
+                f.write('}\n')
+
+                f.write('''
+_lr_goto = {}
+for _k, _v in _lr_goto_items.items():
+   for _x, _y in zip(_v[0], _v[1]):
+       if not _x in _lr_goto: _lr_goto[_x] = {}
+       _lr_goto[_x][_k] = _y
+del _lr_goto_items
+''')
+            else:
+                f.write('\n_lr_goto = { ')
+                for k, v in self.lr_goto.items():
+                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
+                f.write('}\n')
+
+            # Write production table
+            f.write('_lr_productions = [\n')
+            for p in self.lr_productions:
+                if p.func:
+                    f.write('  (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len,
+                                                          p.func, os.path.basename(p.file), p.line))
+                else:
+                    f.write('  (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len))
+            f.write(']\n')
+            f.close()
+
+        except IOError as e:
+            raise
+
+
+    # -----------------------------------------------------------------------------
+    # pickle_table()
+    #
+    # This function pickles the LR parsing tables to a supplied file object
+    # -----------------------------------------------------------------------------
+
+    def pickle_table(self, filename, signature=''):
+        try:
+            import cPickle as pickle
+        except ImportError:
+            import pickle
+        with open(filename, 'wb') as outf:
+            pickle.dump(__tabversion__, outf, pickle_protocol)
+            pickle.dump(self.lr_method, outf, pickle_protocol)
+            pickle.dump(signature, outf, pickle_protocol)
+            pickle.dump(self.lr_action, outf, pickle_protocol)
+            pickle.dump(self.lr_goto, outf, pickle_protocol)
+
+            outp = []
+            for p in self.lr_productions:
+                if p.func:
+                    outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line))
+                else:
+                    outp.append((str(p), p.name, p.len, None, None, None))
+            pickle.dump(outp, outf, pickle_protocol)
+
+# -----------------------------------------------------------------------------
+#                            === INTROSPECTION ===
+#
+# The following functions and classes are used to implement the PLY
+# introspection features followed by the yacc() function itself.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# get_caller_module_dict()
+#
+# This function returns a dictionary containing all of the symbols defined within
+# a caller further down the call stack.  This is used to get the environment
+# associated with the yacc() call if none was provided.
+# -----------------------------------------------------------------------------
+
+def get_caller_module_dict(levels):
+    f = sys._getframe(levels)
+    ldict = f.f_globals.copy()
+    if f.f_globals != f.f_locals:
+        ldict.update(f.f_locals)
+    return ldict
+
+# -----------------------------------------------------------------------------
+# parse_grammar()
+#
+# This takes a raw grammar rule string and parses it into production data
+# -----------------------------------------------------------------------------
+def parse_grammar(doc, file, line):
+    grammar = []
+    # Split the doc string into lines
+    pstrings = doc.splitlines()
+    lastp = None
+    dline = line
+    for ps in pstrings:
+        dline += 1
+        p = ps.split()
+        if not p:
+            continue
+        try:
+            if p[0] == '|':
+                # This is a continuation of a previous rule
+                if not lastp:
+                    raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline))
+                prodname = lastp
+                syms = p[1:]
+            else:
+                prodname = p[0]
+                lastp = prodname
+                syms   = p[2:]
+                assign = p[1]
+                if assign != ':' and assign != '::=':
+                    raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline))
+
+            grammar.append((file, dline, prodname, syms))
+        except SyntaxError:
+            raise
+        except Exception:
+            raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip()))
+
+    return grammar
+
+# -----------------------------------------------------------------------------
+# ParserReflect()
+#
+# This class represents information extracted for building a parser including
+# start symbol, error function, tokens, precedence list, action functions,
+# etc.
+# -----------------------------------------------------------------------------
+class ParserReflect(object):
+    def __init__(self, pdict, log=None):
+        self.pdict      = pdict
+        self.start      = None
+        self.error_func = None
+        self.tokens     = None
+        self.modules    = set()
+        self.grammar    = []
+        self.error      = False
+
+        if log is None:
+            self.log = PlyLogger(sys.stderr)
+        else:
+            self.log = log
+
+    # Get all of the basic information
+    def get_all(self):
+        self.get_start()
+        self.get_error_func()
+        self.get_tokens()
+        self.get_precedence()
+        self.get_pfunctions()
+
+    # Validate all of the information
+    def validate_all(self):
+        self.validate_start()
+        self.validate_error_func()
+        self.validate_tokens()
+        self.validate_precedence()
+        self.validate_pfunctions()
+        self.validate_modules()
+        return self.error
+
+    # Compute a signature over the grammar
+    def signature(self):
+        parts = []
+        try:
+            if self.start:
+                parts.append(self.start)
+            if self.prec:
+                parts.append(''.join([''.join(p) for p in self.prec]))
+            if self.tokens:
+                parts.append(' '.join(self.tokens))
+            for f in self.pfuncs:
+                if f[3]:
+                    parts.append(f[3])
+        except (TypeError, ValueError):
+            pass
+        return ''.join(parts)
+
+    # -----------------------------------------------------------------------------
+    # validate_modules()
+    #
+    # This method checks to see if there are duplicated p_rulename() functions
+    # in the parser module file.  Without this function, it is really easy for
+    # users to make mistakes by cutting and pasting code fragments (and it's a real
+    # bugger to try and figure out why the resulting parser doesn't work).  Therefore,
+    # we just do a little regular expression pattern matching of def statements
+    # to try and detect duplicates.
+    # -----------------------------------------------------------------------------
+
+    def validate_modules(self):
+        # Match def p_funcname(
+        fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')
+
+        for module in self.modules:
+            try:
+                lines, linen = inspect.getsourcelines(module)
+            except IOError:
+                continue
+
+            counthash = {}
+            for linen, line in enumerate(lines):
+                linen += 1
+                m = fre.match(line)
+                if m:
+                    name = m.group(1)
+                    prev = counthash.get(name)
+                    if not prev:
+                        counthash[name] = linen
+                    else:
+                        filename = inspect.getsourcefile(module)
+                        self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d',
+                                         filename, linen, name, prev)
+
+    # Get the start symbol
+    def get_start(self):
+        self.start = self.pdict.get('start')
+
+    # Validate the start symbol
+    def validate_start(self):
+        if self.start is not None:
+            if not isinstance(self.start, string_types):
+                self.log.error("'start' must be a string")
+
+    # Look for error handler
+    def get_error_func(self):
+        self.error_func = self.pdict.get('p_error')
+
+    # Validate the error function
+    def validate_error_func(self):
+        if self.error_func:
+            if isinstance(self.error_func, types.FunctionType):
+                ismethod = 0
+            elif isinstance(self.error_func, types.MethodType):
+                ismethod = 1
+            else:
+                self.log.error("'p_error' defined, but is not a function or method")
+                self.error = True
+                return
+
+            eline = self.error_func.__code__.co_firstlineno
+            efile = self.error_func.__code__.co_filename
+            module = inspect.getmodule(self.error_func)
+            self.modules.add(module)
+
+            argcount = self.error_func.__code__.co_argcount - ismethod
+            if argcount != 1:
+                self.log.error('%s:%d: p_error() requires 1 argument', efile, eline)
+                self.error = True
+
+    # Get the tokens map
+    def get_tokens(self):
+        tokens = self.pdict.get('tokens')
+        if not tokens:
+            self.log.error('No token list is defined')
+            self.error = True
+            return
+
+        if not isinstance(tokens, (list, tuple)):
+            self.log.error('tokens must be a list or tuple')
+            self.error = True
+            return
+
+        if not tokens:
+            self.log.error('tokens is empty')
+            self.error = True
+            return
+
+        self.tokens = tokens
+
+    # Validate the tokens
+    def validate_tokens(self):
+        # Validate the tokens.
+        if 'error' in self.tokens:
+            self.log.error("Illegal token name 'error'. Is a reserved word")
+            self.error = True
+            return
+
+        terminals = set()
+        for n in self.tokens:
+            if n in terminals:
+                self.log.warning('Token %r multiply defined', n)
+            terminals.add(n)
+
+    # Get the precedence map (if any)
+    def get_precedence(self):
+        self.prec = self.pdict.get('precedence')
+
+    # Validate and parse the precedence map
+    def validate_precedence(self):
+        preclist = []
+        if self.prec:
+            if not isinstance(self.prec, (list, tuple)):
+                self.log.error('precedence must be a list or tuple')
+                self.error = True
+                return
+            for level, p in enumerate(self.prec):
+                if not isinstance(p, (list, tuple)):
+                    self.log.error('Bad precedence table')
+                    self.error = True
+                    return
+
+                if len(p) < 2:
+                    self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p)
+                    self.error = True
+                    return
+                assoc = p[0]
+                if not isinstance(assoc, string_types):
+                    self.log.error('precedence associativity must be a string')
+                    self.error = True
+                    return
+                for term in p[1:]:
+                    if not isinstance(term, string_types):
+                        self.log.error('precedence items must be strings')
+                        self.error = True
+                        return
+                    preclist.append((term, assoc, level+1))
+        self.preclist = preclist
+
+    # Get all p_functions from the grammar
+    def get_pfunctions(self):
+        p_functions = []
+        for name, item in self.pdict.items():
+            if not name.startswith('p_') or name == 'p_error':
+                continue
+            if isinstance(item, (types.FunctionType, types.MethodType)):
+                line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno)
+                module = inspect.getmodule(item)
+                p_functions.append((line, module, name, item.__doc__))
+
+        # Sort all of the actions by line number; make sure to stringify
+        # modules to make them sortable, since `line` may not uniquely sort all
+        # p functions
+        p_functions.sort(key=lambda p_function: (
+            p_function[0],
+            str(p_function[1]),
+            p_function[2],
+            p_function[3]))
+        self.pfuncs = p_functions
+
+    # Validate all of the p_functions
+    def validate_pfunctions(self):
+        grammar = []
+        # Check for non-empty symbols
+        if len(self.pfuncs) == 0:
+            self.log.error('no rules of the form p_rulename are defined')
+            self.error = True
+            return
+
+        for line, module, name, doc in self.pfuncs:
+            file = inspect.getsourcefile(module)
+            func = self.pdict[name]
+            if isinstance(func, types.MethodType):
+                reqargs = 2
+            else:
+                reqargs = 1
+            if func.__code__.co_argcount > reqargs:
+                self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__)
+                self.error = True
+            elif func.__code__.co_argcount < reqargs:
+                self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__)
+                self.error = True
+            elif not func.__doc__:
+                self.log.warning('%s:%d: No documentation string specified in function %r (ignored)',
+                                 file, line, func.__name__)
+            else:
+                try:
+                    parsed_g = parse_grammar(doc, file, line)
+                    for g in parsed_g:
+                        grammar.append((name, g))
+                except SyntaxError as e:
+                    self.log.error(str(e))
+                    self.error = True
+
+                # Looks like a valid grammar rule
+                # Mark the file in which defined.
+                self.modules.add(module)
+
+        # Secondary validation step that looks for p_ definitions that are not functions
+        # or functions that look like they might be grammar rules.
+
+        for n, v in self.pdict.items():
+            if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)):
+                continue
+            if n.startswith('t_'):
+                continue
+            if n.startswith('p_') and n != 'p_error':
+                self.log.warning('%r not defined as a function', n)
+            if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or
+                   (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)):
+                if v.__doc__:
+                    try:
+                        doc = v.__doc__.split(' ')
+                        if doc[1] == ':':
+                            self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix',
+                                             v.__code__.co_filename, v.__code__.co_firstlineno, n)
+                    except IndexError:
+                        pass
+
+        self.grammar = grammar
+
+# -----------------------------------------------------------------------------
+# yacc(module)
+#
+# Build a parser
+# -----------------------------------------------------------------------------
+
+def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None,
+         check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file,
+         outputdir=None, debuglog=None, errorlog=None, picklefile=None):
+
+    if tabmodule is None:
+        tabmodule = tab_module
+
+    # Reference to the parsing method of the last built parser
+    global parse
+
+    # If pickling is enabled, table files are not created
+    if picklefile:
+        write_tables = 0
+
+    if errorlog is None:
+        errorlog = PlyLogger(sys.stderr)
+
+    # Get the module dictionary used for the parser
+    if module:
+        _items = [(k, getattr(module, k)) for k in dir(module)]
+        pdict = dict(_items)
+        # If no __file__ attribute is available, try to obtain it from the __module__ instead
+        if '__file__' not in pdict:
+            pdict['__file__'] = sys.modules[pdict['__module__']].__file__
+    else:
+        pdict = get_caller_module_dict(2)
+
+    if outputdir is None:
+        # If no output directory is set, the location of the output files
+        # is determined according to the following rules:
+        #     - If tabmodule specifies a package, files go into that package directory
+        #     - Otherwise, files go in the same directory as the specifying module
+        if isinstance(tabmodule, types.ModuleType):
+            srcfile = tabmodule.__file__
+        else:
+            if '.' not in tabmodule:
+                srcfile = pdict['__file__']
+            else:
+                parts = tabmodule.split('.')
+                pkgname = '.'.join(parts[:-1])
+                exec('import %s' % pkgname)
+                srcfile = getattr(sys.modules[pkgname], '__file__', '')
+        outputdir = os.path.dirname(srcfile)
+
+    # Determine if the module is package of a package or not.
+    # If so, fix the tabmodule setting so that tables load correctly
+    pkg = pdict.get('__package__')
+    if pkg and isinstance(tabmodule, str):
+        if '.' not in tabmodule:
+            tabmodule = pkg + '.' + tabmodule
+
+
+
+    # Set start symbol if it's specified directly using an argument
+    if start is not None:
+        pdict['start'] = start
+
+    # Collect parser information from the dictionary
+    pinfo = ParserReflect(pdict, log=errorlog)
+    pinfo.get_all()
+
+    if pinfo.error:
+        raise YaccError('Unable to build parser')
+
+    # Check signature against table files (if any)
+    signature = pinfo.signature()
+
+    # Read the tables
+    try:
+        lr = LRTable()
+        if picklefile:
+            read_signature = lr.read_pickle(picklefile)
+        else:
+            read_signature = lr.read_table(tabmodule)
+        if optimize or (read_signature == signature):
+            try:
+                lr.bind_callables(pinfo.pdict)
+                parser = LRParser(lr, pinfo.error_func)
+                parse = parser.parse
+                return parser
+            except Exception as e:
+                errorlog.warning('There was a problem loading the table file: %r', e)
+    except VersionError as e:
+        errorlog.warning(str(e))
+    except ImportError:
+        pass
+
+    if debuglog is None:
+        if debug:
+            try:
+                debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w'))
+            except IOError as e:
+                errorlog.warning("Couldn't open %r. %s" % (debugfile, e))
+                debuglog = NullLogger()
+        else:
+            debuglog = NullLogger()
+
+    debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__)
+
+    errors = False
+
+    # Validate the parser information
+    if pinfo.validate_all():
+        raise YaccError('Unable to build parser')
+
+    if not pinfo.error_func:
+        errorlog.warning('no p_error() function is defined')
+
+    # Create a grammar object
+    grammar = Grammar(pinfo.tokens)
+
+    # Set precedence level for terminals
+    for term, assoc, level in pinfo.preclist:
+        try:
+            grammar.set_precedence(term, assoc, level)
+        except GrammarError as e:
+            errorlog.warning('%s', e)
+
+    # Add productions to the grammar
+    for funcname, gram in pinfo.grammar:
+        file, line, prodname, syms = gram
+        try:
+            grammar.add_production(prodname, syms, funcname, file, line)
+        except GrammarError as e:
+            errorlog.error('%s', e)
+            errors = True
+
+    # Set the grammar start symbols
+    try:
+        if start is None:
+            grammar.set_start(pinfo.start)
+        else:
+            grammar.set_start(start)
+    except GrammarError as e:
+        errorlog.error(str(e))
+        errors = True
+
+    if errors:
+        raise YaccError('Unable to build parser')
+
+    # Verify the grammar structure
+    undefined_symbols = grammar.undefined_symbols()
+    for sym, prod in undefined_symbols:
+        errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym)
+        errors = True
+
+    unused_terminals = grammar.unused_terminals()
+    if unused_terminals:
+        debuglog.info('')
+        debuglog.info('Unused terminals:')
+        debuglog.info('')
+        for term in unused_terminals:
+            errorlog.warning('Token %r defined, but not used', term)
+            debuglog.info('    %s', term)
+
+    # Print out all productions to the debug log
+    if debug:
+        debuglog.info('')
+        debuglog.info('Grammar')
+        debuglog.info('')
+        for n, p in enumerate(grammar.Productions):
+            debuglog.info('Rule %-5d %s', n, p)
+
+    # Find unused non-terminals
+    unused_rules = grammar.unused_rules()
+    for prod in unused_rules:
+        errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name)
+
+    if len(unused_terminals) == 1:
+        errorlog.warning('There is 1 unused token')
+    if len(unused_terminals) > 1:
+        errorlog.warning('There are %d unused tokens', len(unused_terminals))
+
+    if len(unused_rules) == 1:
+        errorlog.warning('There is 1 unused rule')
+    if len(unused_rules) > 1:
+        errorlog.warning('There are %d unused rules', len(unused_rules))
+
+    if debug:
+        debuglog.info('')
+        debuglog.info('Terminals, with rules where they appear')
+        debuglog.info('')
+        terms = list(grammar.Terminals)
+        terms.sort()
+        for term in terms:
+            debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]]))
+
+        debuglog.info('')
+        debuglog.info('Nonterminals, with rules where they appear')
+        debuglog.info('')
+        nonterms = list(grammar.Nonterminals)
+        nonterms.sort()
+        for nonterm in nonterms:
+            debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]]))
+        debuglog.info('')
+
+    if check_recursion:
+        unreachable = grammar.find_unreachable()
+        for u in unreachable:
+            errorlog.warning('Symbol %r is unreachable', u)
+
+        infinite = grammar.infinite_cycles()
+        for inf in infinite:
+            errorlog.error('Infinite recursion detected for symbol %r', inf)
+            errors = True
+
+    unused_prec = grammar.unused_precedence()
+    for term, assoc in unused_prec:
+        errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term)
+        errors = True
+
+    if errors:
+        raise YaccError('Unable to build parser')
+
+    # Run the LRGeneratedTable on the grammar
+    if debug:
+        errorlog.debug('Generating %s tables', method)
+
+    lr = LRGeneratedTable(grammar, method, debuglog)
+
+    if debug:
+        num_sr = len(lr.sr_conflicts)
+
+        # Report shift/reduce and reduce/reduce conflicts
+        if num_sr == 1:
+            errorlog.warning('1 shift/reduce conflict')
+        elif num_sr > 1:
+            errorlog.warning('%d shift/reduce conflicts', num_sr)
+
+        num_rr = len(lr.rr_conflicts)
+        if num_rr == 1:
+            errorlog.warning('1 reduce/reduce conflict')
+        elif num_rr > 1:
+            errorlog.warning('%d reduce/reduce conflicts', num_rr)
+
+    # Write out conflicts to the output file
+    if debug and (lr.sr_conflicts or lr.rr_conflicts):
+        debuglog.warning('')
+        debuglog.warning('Conflicts:')
+        debuglog.warning('')
+
+        for state, tok, resolution in lr.sr_conflicts:
+            debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s',  tok, state, resolution)
+
+        already_reported = set()
+        for state, rule, rejected in lr.rr_conflicts:
+            if (state, id(rule), id(rejected)) in already_reported:
+                continue
+            debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
+            debuglog.warning('rejected rule (%s) in state %d', rejected, state)
+            errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
+            errorlog.warning('rejected rule (%s) in state %d', rejected, state)
+            already_reported.add((state, id(rule), id(rejected)))
+
+        warned_never = []
+        for state, rule, rejected in lr.rr_conflicts:
+            if not rejected.reduced and (rejected not in warned_never):
+                debuglog.warning('Rule (%s) is never reduced', rejected)
+                errorlog.warning('Rule (%s) is never reduced', rejected)
+                warned_never.append(rejected)
+
+    # Write the table file if requested
+    if write_tables:
+        try:
+            lr.write_table(tabmodule, outputdir, signature)
+        except IOError as e:
+            errorlog.warning("Couldn't create %r. %s" % (tabmodule, e))
+
+    # Write a pickled version of the tables
+    if picklefile:
+        try:
+            lr.pickle_table(picklefile, signature)
+        except IOError as e:
+            errorlog.warning("Couldn't create %r. %s" % (picklefile, e))
+
+    # Build the parser
+    lr.bind_callables(pinfo.pdict)
+    parser = LRParser(lr, pinfo.error_func)
+
+    parse = parser.parse
+    return parser
diff --git a/.venv/lib/python3.8/site-packages/pycparser/ply/ygen.py b/.venv/lib/python3.8/site-packages/pycparser/ply/ygen.py
new file mode 100644
index 0000000000000000000000000000000000000000..acf5ca1a37b69389256227c570c65eed96e3228e
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/ply/ygen.py
@@ -0,0 +1,74 @@
+# ply: ygen.py
+#
+# This is a support program that auto-generates different versions of the YACC parsing
+# function with different features removed for the purposes of performance.
+#
+# Users should edit the method LParser.parsedebug() in yacc.py.   The source code 
+# for that method is then used to create the other methods.   See the comments in
+# yacc.py for further details.
+
+import os.path
+import shutil
+
+def get_source_range(lines, tag):
+    srclines = enumerate(lines)
+    start_tag = '#--! %s-start' % tag
+    end_tag = '#--! %s-end' % tag
+
+    for start_index, line in srclines:
+        if line.strip().startswith(start_tag):
+            break
+
+    for end_index, line in srclines:
+        if line.strip().endswith(end_tag):
+            break
+
+    return (start_index + 1, end_index)
+
+def filter_section(lines, tag):
+    filtered_lines = []
+    include = True
+    tag_text = '#--! %s' % tag
+    for line in lines:
+        if line.strip().startswith(tag_text):
+            include = not include
+        elif include:
+            filtered_lines.append(line)
+    return filtered_lines
+
+def main():
+    dirname = os.path.dirname(__file__)
+    shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak'))
+    with open(os.path.join(dirname, 'yacc.py'), 'r') as f:
+        lines = f.readlines()
+
+    parse_start, parse_end = get_source_range(lines, 'parsedebug')
+    parseopt_start, parseopt_end = get_source_range(lines, 'parseopt')
+    parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack')
+
+    # Get the original source
+    orig_lines = lines[parse_start:parse_end]
+
+    # Filter the DEBUG sections out
+    parseopt_lines = filter_section(orig_lines, 'DEBUG')
+
+    # Filter the TRACKING sections out
+    parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING')
+
+    # Replace the parser source sections with updated versions
+    lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines
+    lines[parseopt_start:parseopt_end] = parseopt_lines
+
+    lines = [line.rstrip()+'\n' for line in lines]
+    with open(os.path.join(dirname, 'yacc.py'), 'w') as f:
+        f.writelines(lines)
+
+    print('Updated yacc.py')
+
+if __name__ == '__main__':
+    main()
+
+
+
+
+
diff --git a/.venv/lib/python3.8/site-packages/pycparser/plyparser.py b/.venv/lib/python3.8/site-packages/pycparser/plyparser.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8f4c4395ea7ed07572aeb3a9d9064c0373b504b
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/plyparser.py
@@ -0,0 +1,133 @@
+#-----------------------------------------------------------------
+# plyparser.py
+#
+# PLYParser class and other utilities for simplifying programming
+# parsers with PLY
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+import warnings
+
+class Coord(object):
+    """ Coordinates of a syntactic element. Consists of:
+            - File name
+            - Line number
+            - (optional) column number, for the Lexer
+    """
+    __slots__ = ('file', 'line', 'column', '__weakref__')
+    def __init__(self, file, line, column=None):
+        self.file = file
+        self.line = line
+        self.column = column
+
+    def __str__(self):
+        str = "%s:%s" % (self.file, self.line)
+        if self.column: str += ":%s" % self.column
+        return str
+
+
+class ParseError(Exception): pass
+
+
+class PLYParser(object):
+    def _create_opt_rule(self, rulename):
+        """ Given a rule name, creates an optional ply.yacc rule
+            for it. The name of the optional rule is
+            <rulename>_opt
+        """
+        optname = rulename + '_opt'
+
+        def optrule(self, p):
+            p[0] = p[1]
+
+        optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename)
+        optrule.__name__ = 'p_%s' % optname
+        setattr(self.__class__, optrule.__name__, optrule)
+
+    def _coord(self, lineno, column=None):
+        return Coord(
+                file=self.clex.filename,
+                line=lineno,
+                column=column)
+
+    def _token_coord(self, p, token_idx):
+        """ Returns the coordinates for the YaccProduction object 'p' indexed
+            with 'token_idx'. The coordinate includes the 'lineno' and
+            'column'. Both follow the lex semantic, starting from 1.
+        """
+        last_cr = p.lexer.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx))
+        if last_cr < 0:
+            last_cr = -1
+        column = (p.lexpos(token_idx) - (last_cr))
+        return self._coord(p.lineno(token_idx), column)
+
+    def _parse_error(self, msg, coord):
+        raise ParseError("%s: %s" % (coord, msg))
+
+
+def parameterized(*params):
+    """ Decorator to create parameterized rules.
+
+    Parameterized rule methods must be named starting with 'p_' and contain
+    'xxx', and their docstrings may contain 'xxx' and 'yyy'. These will be
+    replaced by the given parameter tuples. For example, ``p_xxx_rule()`` with
+    docstring 'xxx_rule  : yyy' when decorated with
+    ``@parameterized(('id', 'ID'))`` produces ``p_id_rule()`` with the docstring
+    'id_rule  : ID'. Using multiple tuples produces multiple rules.
+    """
+    def decorate(rule_func):
+        rule_func._params = params
+        return rule_func
+    return decorate
+
+
+def template(cls):
+    """ Class decorator to generate rules from parameterized rule templates.
+
+    See `parameterized` for more information on parameterized rules.
+    """
+    issued_nodoc_warning = False
+    for attr_name in dir(cls):
+        if attr_name.startswith('p_'):
+            method = getattr(cls, attr_name)
+            if hasattr(method, '_params'):
+                # Remove the template method
+                delattr(cls, attr_name)
+                # Create parameterized rules from this method; only run this if
+                # the method has a docstring. This is to address an issue when
+                # pycparser's users are installed in -OO mode which strips
+                # docstrings away.
+                # See: https://github.com/eliben/pycparser/pull/198/ and
+                #      https://github.com/eliben/pycparser/issues/197
+                # for discussion.
+                if method.__doc__ is not None:
+                    _create_param_rules(cls, method)
+                elif not issued_nodoc_warning:
+                    warnings.warn(
+                        'parsing methods must have __doc__ for pycparser to work properly',
+                        RuntimeWarning,
+                        stacklevel=2)
+                    issued_nodoc_warning = True
+    return cls
+
+
+def _create_param_rules(cls, func):
+    """ Create ply.yacc rules based on a parameterized rule function
+
+    Generates new methods (one per each pair of parameters) based on the
+    template rule function `func`, and attaches them to `cls`. The rule
+    function's parameters must be accessible via its `_params` attribute.
+    """
+    for xxx, yyy in func._params:
+        # Use the template method's body for each new method
+        def param_rule(self, p):
+            func(self, p)
+
+        # Substitute in the params for the grammar rule and function name
+        param_rule.__doc__ = func.__doc__.replace('xxx', xxx).replace('yyy', yyy)
+        param_rule.__name__ = func.__name__.replace('xxx', xxx)
+
+        # Attach the new method to the class
+        setattr(cls, param_rule.__name__, param_rule)
diff --git a/.venv/lib/python3.8/site-packages/pycparser/yacctab.py b/.venv/lib/python3.8/site-packages/pycparser/yacctab.py
new file mode 100644
index 0000000000000000000000000000000000000000..68b14660887561f90702ac091fe44366e12ba319
--- /dev/null
+++ b/.venv/lib/python3.8/site-packages/pycparser/yacctab.py
@@ -0,0 +1,369 @@
+
+# yacctab.py
+# This file is automatically generated. Do not edit.
+_tabversion = '3.10'
+
+_lr_method = 'LALR'
+
+_lr_signature = 'translation_unit_or_emptyleftLORleftLANDleftORleftXORleftANDleftEQNEleftGTGELTLEleftRSHIFTLSHIFTleftPLUSMINUSleftTIMESDIVIDEMODAUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN FLOAT FOR GOTO IF INLINE INT LONG REGISTER OFFSETOF RESTRICT RETURN SHORT SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE __INT128 _BOOL _COMPLEX _NORETURN _THREAD_LOCAL _STATIC_ASSERT _ATOMIC _ALIGNOF _ALIGNAS _PRAGMA ID TYPEID INT_CONST_DEC INT_CONST_OCT INT_CONST_HEX INT_CONST_BIN INT_CONST_CHAR FLOAT_CONST HEX_FLOAT_CONST CHAR_CONST WCHAR_CONST U8CHAR_CONST U16CHAR_CONST U32CHAR_CONST STRING_LITERAL WSTRING_LITERAL U8STRING_LITERAL U16STRING_LITERAL U32STRING_LITERAL PLUS MINUS TIMES DIVIDE MOD OR AND NOT XOR LSHIFT RSHIFT LOR LAND LNOT LT LE GT GE EQ NE EQUALS TIMESEQUAL DIVEQUAL MODEQUAL PLUSEQUAL MINUSEQUAL LSHIFTEQUAL RSHIFTEQUAL ANDEQUAL XOREQUAL OREQUAL PLUSPLUS MINUSMINUS ARROW CONDOP LPAREN RPAREN LBRACKET RBRACKET LBRACE RBRACE COMMA PERIOD SEMI COLON ELLIPSIS PPHASH PPPRAGMA PPPRAGMASTRabstract_declarator_opt : empty\n| abstract_declaratorassignment_expression_opt : empty\n| assignment_expressionblock_item_list_opt : empty\n| block_item_listdeclaration_list_opt : empty\n| declaration_listdeclaration_specifiers_no_type_opt : empty\n| declaration_specifiers_no_typedesignation_opt : empty\n| designationexpression_opt : empty\n| expressionid_init_declarator_list_opt : empty\n| id_init_declarator_listidentifier_list_opt : empty\n| identifier_listinit_declarator_list_opt : empty\n| init_declarator_listinitializer_list_opt : empty\n| initializer_listparameter_type_list_opt : empty\n| parameter_type_liststruct_declarator_list_opt : empty\n| struct_declarator_listtype_qualifier_list_opt : empty\n| type_qualifier_list direct_id_declarator   : ID\n         direct_id_declarator   : LPAREN id_declarator RPAREN\n         direct_id_declarator   : direct_id_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n         direct_id_declarator   : direct_id_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n                                    | direct_id_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n         direct_id_declarator   : direct_id_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n         direct_id_declarator   : direct_id_declarator LPAREN parameter_type_list RPAREN\n                                    | direct_id_declarator LPAREN identifier_list_opt RPAREN\n         direct_typeid_declarator   : TYPEID\n         direct_typeid_declarator   : LPAREN typeid_declarator RPAREN\n         direct_typeid_declarator   : direct_typeid_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n         direct_typeid_declarator   : direct_typeid_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n                                    | direct_typeid_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n         direct_typeid_declarator   : direct_typeid_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n         direct_typeid_declarator   : direct_typeid_declarator LPAREN parameter_type_list RPAREN\n                                    | direct_typeid_declarator LPAREN identifier_list_opt RPAREN\n         direct_typeid_noparen_declarator   : TYPEID\n         direct_typeid_noparen_declarator   : direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n         direct_typeid_noparen_declarator   : direct_typeid_noparen_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n                                    | direct_typeid_noparen_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n         direct_typeid_noparen_declarator   : direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n         direct_typeid_noparen_declarator   : direct_typeid_noparen_declarator LPAREN parameter_type_list RPAREN\n                                    | direct_typeid_noparen_declarator LPAREN identifier_list_opt RPAREN\n         id_declarator  : direct_id_declarator\n         id_declarator  : pointer direct_id_declarator\n         typeid_declarator  : direct_typeid_declarator\n         typeid_declarator  : pointer direct_typeid_declarator\n         typeid_noparen_declarator  : direct_typeid_noparen_declarator\n         typeid_noparen_declarator  : pointer direct_typeid_noparen_declarator\n         translation_unit_or_empty   : translation_unit\n                                        | empty\n         translation_unit    : external_declaration\n         translation_unit    : translation_unit external_declaration\n         external_declaration    : function_definition\n         external_declaration    : declaration\n         external_declaration    : pp_directive\n                                    | pppragma_directive\n         external_declaration    : SEMI\n         external_declaration    : static_assert\n         static_assert           : _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN\n                                    | _STATIC_ASSERT LPAREN constant_expression RPAREN\n         pp_directive  : PPHASH\n         pppragma_directive      : PPPRAGMA\n                                    | PPPRAGMA PPPRAGMASTR\n                                    | _PRAGMA LPAREN unified_string_literal RPAREN\n         pppragma_directive_list : pppragma_directive\n                                    | pppragma_directive_list pppragma_directive\n         function_definition : id_declarator declaration_list_opt compound_statement\n         function_definition : declaration_specifiers id_declarator declaration_list_opt compound_statement\n         statement   : labeled_statement\n                        | expression_statement\n                        | compound_statement\n                        | selection_statement\n                        | iteration_statement\n                        | jump_statement\n                        | pppragma_directive\n                        | static_assert\n         pragmacomp_or_statement     : pppragma_directive_list statement\n                                        | statement\n         decl_body : declaration_specifiers init_declarator_list_opt\n                      | declaration_specifiers_no_type id_init_declarator_list_opt\n         declaration : decl_body SEMI\n         declaration_list    : declaration\n                                | declaration_list declaration\n         declaration_specifiers_no_type  : type_qualifier declaration_specifiers_no_type_opt\n         declaration_specifiers_no_type  : storage_class_specifier declaration_specifiers_no_type_opt\n         declaration_specifiers_no_type  : function_specifier declaration_specifiers_no_type_opt\n         declaration_specifiers_no_type  : atomic_specifier declaration_specifiers_no_type_opt\n         declaration_specifiers_no_type  : alignment_specifier declaration_specifiers_no_type_opt\n         declaration_specifiers  : declaration_specifiers type_qualifier\n         declaration_specifiers  : declaration_specifiers storage_class_specifier\n         declaration_specifiers  : declaration_specifiers function_specifier\n         declaration_specifiers  : declaration_specifiers type_specifier_no_typeid\n         declaration_specifiers  : type_specifier\n         declaration_specifiers  : declaration_specifiers_no_type type_specifier\n         declaration_specifiers  : declaration_specifiers alignment_specifier\n         storage_class_specifier : AUTO\n                                    | REGISTER\n                                    | STATIC\n                                    | EXTERN\n                                    | TYPEDEF\n                                    | _THREAD_LOCAL\n         function_specifier  : INLINE\n                                | _NORETURN\n         type_specifier_no_typeid  : VOID\n                                      | _BOOL\n                                      | CHAR\n                                      | SHORT\n                                      | INT\n                                      | LONG\n                                      | FLOAT\n                                      | DOUBLE\n                                      | _COMPLEX\n                                      | SIGNED\n                                      | UNSIGNED\n                                      | __INT128\n         type_specifier  : typedef_name\n                            | enum_specifier\n                            | struct_or_union_specifier\n                            | type_specifier_no_typeid\n                            | atomic_specifier\n         atomic_specifier  : _ATOMIC LPAREN type_name RPAREN\n         type_qualifier  : CONST\n                            | RESTRICT\n                            | VOLATILE\n                            | _ATOMIC\n         init_declarator_list    : init_declarator\n                                    | init_declarator_list COMMA init_declarator\n         init_declarator : declarator\n                            | declarator EQUALS initializer\n         id_init_declarator_list    : id_init_declarator\n                                       | id_init_declarator_list COMMA init_declarator\n         id_init_declarator : id_declarator\n                               | id_declarator EQUALS initializer\n         specifier_qualifier_list    : specifier_qualifier_list type_specifier_no_typeid\n         specifier_qualifier_list    : specifier_qualifier_list type_qualifier\n         specifier_qualifier_list  : type_specifier\n         specifier_qualifier_list  : type_qualifier_list type_specifier\n         specifier_qualifier_list  : alignment_specifier\n         specifier_qualifier_list  : specifier_qualifier_list alignment_specifier\n         struct_or_union_specifier   : struct_or_union ID\n                                        | struct_or_union TYPEID\n         struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close\n                                      | struct_or_union brace_open brace_close\n         struct_or_union_specifier   : struct_or_union ID brace_open struct_declaration_list brace_close\n                                        | struct_or_union ID brace_open brace_close\n                                        | struct_or_union TYPEID brace_open struct_declaration_list brace_close\n                                        | struct_or_union TYPEID brace_open brace_close\n         struct_or_union : STRUCT\n                            | UNION\n         struct_declaration_list     : struct_declaration\n                                        | struct_declaration_list struct_declaration\n         struct_declaration : specifier_qualifier_list struct_declarator_list_opt SEMI\n         struct_declaration : SEMI\n         struct_declaration : pppragma_directive\n         struct_declarator_list  : struct_declarator\n                                    | struct_declarator_list COMMA struct_declarator\n         struct_declarator : declarator\n         struct_declarator   : declarator COLON constant_expression\n                                | COLON constant_expression\n         enum_specifier  : ENUM ID\n                            | ENUM TYPEID\n         enum_specifier  : ENUM brace_open enumerator_list brace_close\n         enum_specifier  : ENUM ID brace_open enumerator_list brace_close\n                            | ENUM TYPEID brace_open enumerator_list brace_close\n         enumerator_list : enumerator\n                            | enumerator_list COMMA\n                            | enumerator_list COMMA enumerator\n         alignment_specifier  : _ALIGNAS LPAREN type_name RPAREN\n                                 | _ALIGNAS LPAREN constant_expression RPAREN\n         enumerator  : ID\n                        | ID EQUALS constant_expression\n         declarator  : id_declarator\n                        | typeid_declarator\n         pointer : TIMES type_qualifier_list_opt\n                    | TIMES type_qualifier_list_opt pointer\n         type_qualifier_list : type_qualifier\n                                | type_qualifier_list type_qualifier\n         parameter_type_list : parameter_list\n                                | parameter_list COMMA ELLIPSIS\n         parameter_list  : parameter_declaration\n                            | parameter_list COMMA parameter_declaration\n         parameter_declaration   : declaration_specifiers id_declarator\n                                    | declaration_specifiers typeid_noparen_declarator\n         parameter_declaration   : declaration_specifiers abstract_declarator_opt\n         identifier_list : identifier\n                            | identifier_list COMMA identifier\n         initializer : assignment_expression\n         initializer : brace_open initializer_list_opt brace_close\n                        | brace_open initializer_list COMMA brace_close\n         initializer_list    : designation_opt initializer\n                                | initializer_list COMMA designation_opt initializer\n         designation : designator_list EQUALS\n         designator_list : designator\n                            | designator_list designator\n         designator  : LBRACKET constant_expression RBRACKET\n                        | PERIOD identifier\n         type_name   : specifier_qualifier_list abstract_declarator_opt\n         abstract_declarator     : pointer\n         abstract_declarator     : pointer direct_abstract_declarator\n         abstract_declarator     : direct_abstract_declarator\n         direct_abstract_declarator  : LPAREN abstract_declarator RPAREN  direct_abstract_declarator  : direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET\n         direct_abstract_declarator  : LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n         direct_abstract_declarator  : direct_abstract_declarator LBRACKET TIMES RBRACKET\n         direct_abstract_declarator  : LBRACKET TIMES RBRACKET\n         direct_abstract_declarator  : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN\n         direct_abstract_declarator  : LPAREN parameter_type_list_opt RPAREN\n         block_item  : declaration\n                        | statement\n         block_item_list : block_item\n                            | block_item_list block_item\n         compound_statement : brace_open block_item_list_opt brace_close  labeled_statement : ID COLON pragmacomp_or_statement  labeled_statement : CASE constant_expression COLON pragmacomp_or_statement  labeled_statement : DEFAULT COLON pragmacomp_or_statement  selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement  selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement  selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement  iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement  iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI  iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement  iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement  jump_statement  : GOTO ID SEMI  jump_statement  : BREAK SEMI  jump_statement  : CONTINUE SEMI  jump_statement  : RETURN expression SEMI\n                            | RETURN SEMI\n         expression_statement : expression_opt SEMI  expression  : assignment_expression\n                        | expression COMMA assignment_expression\n         assignment_expression : LPAREN compound_statement RPAREN  typedef_name : TYPEID  assignment_expression   : conditional_expression\n                                    | unary_expression assignment_operator assignment_expression\n         assignment_operator : EQUALS\n                                | XOREQUAL\n                                | TIMESEQUAL\n                                | DIVEQUAL\n                                | MODEQUAL\n                                | PLUSEQUAL\n                                | MINUSEQUAL\n                                | LSHIFTEQUAL\n                                | RSHIFTEQUAL\n                                | ANDEQUAL\n                                | OREQUAL\n         constant_expression : conditional_expression  conditional_expression  : binary_expression\n                                    | binary_expression CONDOP expression COLON conditional_expression\n         binary_expression   : cast_expression\n                                | binary_expression TIMES binary_expression\n                                | binary_expression DIVIDE binary_expression\n                                | binary_expression MOD binary_expression\n                                | binary_expression PLUS binary_expression\n                                | binary_expression MINUS binary_expression\n                                | binary_expression RSHIFT binary_expression\n                                | binary_expression LSHIFT binary_expression\n                                | binary_expression LT binary_expression\n                                | binary_expression LE binary_expression\n                                | binary_expression GE binary_expression\n                                | binary_expression GT binary_expression\n                                | binary_expression EQ binary_expression\n                                | binary_expression NE binary_expression\n                                | binary_expression AND binary_expression\n                                | binary_expression OR binary_expression\n                                | binary_expression XOR binary_expression\n                                | binary_expression LAND binary_expression\n                                | binary_expression LOR binary_expression\n         cast_expression : unary_expression  cast_expression : LPAREN type_name RPAREN cast_expression  unary_expression    : postfix_expression  unary_expression    : PLUSPLUS unary_expression\n                                | MINUSMINUS unary_expression\n                                | unary_operator cast_expression\n         unary_expression    : SIZEOF unary_expression\n                                | SIZEOF LPAREN type_name RPAREN\n                                | _ALIGNOF LPAREN type_name RPAREN\n         unary_operator  : AND\n                            | TIMES\n                            | PLUS\n                            | MINUS\n                            | NOT\n                            | LNOT\n         postfix_expression  : primary_expression  postfix_expression  : postfix_expression LBRACKET expression RBRACKET  postfix_expression  : postfix_expression LPAREN argument_expression_list RPAREN\n                                | postfix_expression LPAREN RPAREN\n         postfix_expression  : postfix_expression PERIOD ID\n                                | postfix_expression PERIOD TYPEID\n                                | postfix_expression ARROW ID\n                                | postfix_expression ARROW TYPEID\n         postfix_expression  : postfix_expression PLUSPLUS\n                                | postfix_expression MINUSMINUS\n         postfix_expression  : LPAREN type_name RPAREN brace_open initializer_list brace_close\n                                | LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close\n         primary_expression  : identifier  primary_expression  : constant  primary_expression  : unified_string_literal\n                                | unified_wstring_literal\n         primary_expression  : LPAREN expression RPAREN  primary_expression  : OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN\n         offsetof_member_designator : identifier\n                                         | offsetof_member_designator PERIOD identifier\n                                         | offsetof_member_designator LBRACKET expression RBRACKET\n         argument_expression_list    : assignment_expression\n                                        | argument_expression_list COMMA assignment_expression\n         identifier  : ID  constant    : INT_CONST_DEC\n                        | INT_CONST_OCT\n                        | INT_CONST_HEX\n                        | INT_CONST_BIN\n                        | INT_CONST_CHAR\n         constant    : FLOAT_CONST\n                        | HEX_FLOAT_CONST\n         constant    : CHAR_CONST\n                        | WCHAR_CONST\n                        | U8CHAR_CONST\n                        | U16CHAR_CONST\n                        | U32CHAR_CONST\n         unified_string_literal  : STRING_LITERAL\n                                    | unified_string_literal STRING_LITERAL\n         unified_wstring_literal : WSTRING_LITERAL\n                                    | U8STRING_LITERAL\n                                    | U16STRING_LITERAL\n                                    | U32STRING_LITERAL\n                                    | unified_wstring_literal WSTRING_LITERAL\n                                    | unified_wstring_literal U8STRING_LITERAL\n                                    | unified_wstring_literal U16STRING_LITERAL\n                                    | unified_wstring_literal U32STRING_LITERAL\n         brace_open  :   LBRACE\n         brace_close :   RBRACE\n        empty : '
+    
+_lr_action_items = {'$end':([0,1,2,3,4,5,6,7,8,9,10,14,15,64,90,91,127,208,251,262,267,355,499,],[-340,0,-58,-59,-60,-62,-63,-64,-65,-66,-67,-70,-71,-61,-90,-72,-76,-339,-77,-73,-69,-221,-68,]),'SEMI':([0,2,4,5,6,7,8,9,10,12,13,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,69,70,71,72,73,74,75,76,77,78,79,81,83,84,85,86,87,88,89,90,91,97,98,99,100,101,102,103,104,105,106,107,108,110,111,112,117,118,119,121,122,123,124,127,128,130,132,139,140,143,144,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,203,204,205,206,207,208,209,210,211,212,214,220,221,222,223,224,225,226,227,228,229,230,231,232,233,236,239,242,245,246,247,248,249,250,251,252,253,254,255,262,263,267,291,292,293,295,296,297,300,301,302,303,311,312,326,327,330,333,334,335,336,337,338,339,340,341,342,343,344,345,346,348,349,353,354,355,356,357,358,360,361,369,370,371,372,373,374,375,376,377,403,404,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,439,440,459,460,463,464,465,468,469,470,471,473,475,479,480,481,482,483,484,485,486,493,494,497,499,501,502,505,506,508,509,522,523,524,525,526,527,529,530,531,535,536,538,552,553,554,555,556,558,561,563,570,571,574,579,580,582,584,585,586,],[9,9,-60,-62,-63,-64,-65,-66,-67,-340,90,-70,-71,-52,-340,-340,-340,-128,-102,-340,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,-340,-340,-129,-134,-181,-98,-99,-100,-101,-104,-88,-134,-19,-20,-135,-137,-182,-54,-37,-90,-72,-53,-93,-9,-10,-340,-94,-95,-103,-89,-129,-15,-16,-139,-141,-97,-96,-169,-170,-338,-149,-150,210,-76,-340,-181,-55,-328,-30,-306,-255,-256,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,210,210,210,-152,-159,-339,-340,-162,-163,-145,-147,-13,-340,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,361,-14,-340,374,375,377,-238,-242,-277,-77,-38,-136,-138,-196,-73,-329,-69,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-35,-36,-140,-142,-171,210,-154,210,-156,-151,-160,465,-143,-144,-148,-25,-26,-164,-166,-146,-130,-177,-178,-221,-220,-13,-340,-340,-237,-340,-87,-74,-340,483,-233,-234,484,-236,-43,-44,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,-295,-296,-297,-298,-299,-31,-34,-172,-173,-153,-155,-161,-168,-222,-340,-224,-240,-239,-86,-75,529,-340,-232,-235,-243,-197,-39,-42,-278,-68,-293,-294,-284,-285,-32,-33,-165,-167,-223,-340,-340,-340,-340,559,-198,-40,-41,-257,-225,-87,-74,-227,-228,572,-302,-309,-340,580,-303,-226,-229,-340,-340,-231,-230,]),'PPHASH':([0,2,4,5,6,7,8,9,10,14,15,64,90,91,127,208,251,262,267,355,499,],[14,14,-60,-62,-63,-64,-65,-66,-67,-70,-71,-61,-90,-72,-76,-339,-77,-73,-69,-221,-68,]),'PPPRAGMA':([0,2,4,5,6,7,8,9,10,14,15,64,90,91,121,124,127,128,203,204,205,207,208,210,211,221,222,223,224,225,226,227,228,229,230,231,232,242,251,262,267,333,335,338,355,356,358,360,361,369,370,371,374,375,377,465,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[15,15,-60,-62,-63,-64,-65,-66,-67,-70,-71,-61,-90,-72,-338,15,-76,15,15,15,15,-159,-339,-162,-163,15,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,15,-77,-73,-69,15,15,-160,-221,-220,15,15,-237,15,-87,-74,-233,-234,-236,-161,-222,15,-224,-86,-75,-232,-235,-68,-223,15,15,15,-225,-87,-74,-227,-228,15,-226,-229,15,15,-231,-230,]),'_PRAGMA':([0,2,4,5,6,7,8,9,10,14,15,64,90,91,121,124,127,128,203,204,205,207,208,210,211,221,222,223,224,225,226,227,228,229,230,231,232,242,251,262,267,333,335,338,355,356,358,360,361,369,370,371,374,375,377,465,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[16,16,-60,-62,-63,-64,-65,-66,-67,-70,-71,-61,-90,-72,-338,16,-76,16,16,16,16,-159,-339,-162,-163,16,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,16,-77,-73,-69,16,16,-160,-221,-220,16,16,-237,16,-87,-74,-233,-234,-236,-161,-222,16,-224,-86,-75,-232,-235,-68,-223,16,16,16,-225,-87,-74,-227,-228,16,-226,-229,16,16,-231,-230,]),'_STATIC_ASSERT':([0,2,4,5,6,7,8,9,10,14,15,64,90,91,121,127,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,251,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[18,18,-60,-62,-63,-64,-65,-66,-67,-70,-71,-61,-90,-72,-338,-76,18,-339,18,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,18,-77,-73,-69,-221,-220,18,18,-237,18,-87,-74,-233,-234,-236,-222,18,-224,-86,-75,-232,-235,-68,-223,18,18,18,-225,-87,-74,-227,-228,18,-226,-229,18,18,-231,-230,]),'ID':([0,2,4,5,6,7,8,9,10,12,14,15,17,20,21,22,23,24,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,62,63,64,69,70,71,72,74,75,76,77,78,80,81,82,90,91,94,95,96,98,99,100,101,102,103,104,106,112,113,114,115,116,117,118,119,120,121,122,123,126,127,128,134,135,136,137,141,147,148,149,150,153,154,155,156,160,161,182,183,184,192,194,195,196,197,198,199,206,208,209,212,214,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,244,247,251,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,294,298,306,309,310,314,318,322,323,330,331,332,334,336,337,340,341,342,347,348,349,353,354,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,398,400,401,402,405,448,449,452,455,457,459,460,463,464,466,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,507,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,564,565,570,572,579,580,582,584,585,586,],[28,28,-60,-62,-63,-64,-65,-66,-67,28,-70,-71,28,28,-340,-340,-340,-128,-102,28,-340,-107,-340,-125,-126,-127,-129,-241,118,122,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-157,-158,-61,28,28,-129,-134,-98,-99,-100,-101,-104,28,-134,28,-90,-72,159,-340,159,-93,-9,-10,-340,-94,-95,-103,-129,-97,-183,-27,-28,-185,-96,-169,-170,202,-338,-149,-150,159,-76,233,28,159,-340,159,159,-287,-288,-289,-286,159,159,159,159,-290,-291,159,-340,-28,28,28,159,-184,-186,202,202,-152,-339,28,-145,-147,233,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,159,159,233,373,159,-77,-340,159,-340,-28,-73,-69,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,431,433,159,159,-287,159,159,159,28,28,-340,-171,202,159,-154,-156,-151,-143,-144,-148,159,-146,-130,-177,-178,-221,-220,233,233,-237,159,159,159,159,233,-87,-74,159,-233,-234,-236,159,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,159,-12,159,159,-287,159,159,159,-340,159,28,159,159,-172,-173,-153,-155,28,159,-222,233,-224,159,-86,-75,159,-232,-235,-340,-201,-340,-68,159,159,159,159,-340,-28,-287,-223,233,233,233,159,159,159,-11,-287,159,159,-225,-87,-74,-227,-228,159,-340,159,159,233,159,-226,-229,233,233,-231,-230,]),'LPAREN':([0,2,4,5,6,7,8,9,10,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,64,69,70,71,72,74,75,76,77,78,80,81,82,88,89,90,91,94,95,97,98,99,100,101,102,103,104,106,109,112,113,114,115,116,117,118,119,121,122,123,126,127,128,132,134,135,136,139,140,141,143,147,148,149,150,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,192,194,195,196,197,206,208,209,212,214,216,221,222,223,224,225,226,227,228,229,230,231,232,233,234,237,238,240,241,242,243,247,251,252,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,294,298,300,301,302,303,306,309,310,311,312,318,319,322,323,324,325,330,332,334,336,337,340,341,342,347,348,349,351,352,353,354,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,403,404,405,406,429,431,432,433,434,439,440,446,447,448,452,455,457,459,460,463,464,466,467,469,470,471,474,478,479,480,482,483,484,487,489,493,494,498,499,500,501,502,503,508,509,510,511,512,515,516,518,520,524,525,526,527,528,529,532,533,535,536,543,544,545,546,547,548,549,550,551,552,553,554,555,556,559,561,562,563,565,566,567,570,572,574,577,578,579,580,582,584,585,586,],[17,17,-60,-62,-63,-64,-65,-66,-67,82,-70,-71,92,17,94,96,17,-340,-340,-340,-128,-102,17,-340,-29,-107,-340,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,125,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,126,-61,82,17,-129,125,-98,-99,-100,-101,-104,82,-134,82,137,-37,-90,-72,141,-340,96,-93,-9,-10,-340,-94,-95,-103,-129,125,-97,-183,-27,-28,-185,-96,-169,-170,-338,-149,-150,141,-76,238,137,82,238,-340,-328,-30,238,-306,-287,-288,-289,-286,288,294,294,141,298,299,-292,-315,-290,-291,-304,-305,-307,304,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,238,-340,-28,322,82,238,-184,-186,-152,-339,82,-145,-147,351,238,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,141,362,238,366,367,238,372,238,-77,-38,-340,238,-340,-28,-73,-329,-69,238,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,238,238,-300,-301,238,238,-334,-335,-336,-337,-287,238,238,-35,-36,322,449,322,-340,-45,458,-171,141,-154,-156,-151,-143,-144,-148,141,-146,-130,351,351,-177,-178,-221,-220,238,238,-237,238,238,238,238,238,-87,-74,238,-233,-234,-236,238,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,238,-12,141,-287,238,238,-43,-44,141,-308,-295,-296,-297,-298,-299,-31,-34,449,458,-340,322,238,238,-172,-173,-153,-155,82,141,-222,238,-224,141,528,-86,-75,238,-232,-235,-340,-201,-39,-42,-340,-68,141,-293,-294,238,-32,-33,238,-340,-28,-210,-216,-214,-287,-223,238,238,238,238,238,238,-11,-40,-41,-287,238,238,-50,-51,-212,-211,-213,-215,-225,-87,-74,-227,-228,238,-302,-340,-309,238,-46,-49,238,238,-303,-47,-48,-226,-229,238,238,-231,-230,]),'TIMES':([0,2,4,5,6,7,8,9,10,12,14,15,17,21,22,23,24,25,26,27,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,69,70,71,72,74,75,76,77,78,81,82,90,91,94,95,98,99,100,101,102,103,104,106,112,113,114,115,116,117,118,119,121,122,123,126,127,128,134,135,136,139,141,143,145,146,147,148,149,150,151,152,153,154,155,156,158,159,160,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,192,194,195,197,206,208,209,212,214,216,221,222,223,224,225,226,227,228,229,230,231,232,233,234,238,242,247,250,251,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,293,294,295,296,297,298,300,301,302,303,306,309,310,322,323,330,332,334,336,337,340,341,342,347,348,349,351,353,354,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,448,455,457,459,460,463,464,466,467,469,470,471,474,479,480,482,483,484,487,489,497,498,499,500,501,502,503,505,506,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,561,562,563,565,570,572,574,579,580,582,584,585,586,],[30,30,-60,-62,-63,-64,-65,-66,-67,30,-70,-71,30,-340,-340,-340,-128,-102,30,-340,-107,-340,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,30,30,-129,-134,-98,-99,-100,-101,-104,-134,30,-90,-72,147,-340,-93,-9,-10,-340,-94,-95,-103,-129,-97,30,-27,-28,-185,-96,-169,-170,-338,-149,-150,147,-76,147,30,147,-340,-328,147,-306,269,-258,-287,-288,-289,-286,-277,-279,147,147,147,147,-292,-315,-290,-291,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,306,-340,-28,30,30,147,-186,-152,-339,30,-145,-147,30,147,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,147,147,147,147,-277,-77,-340,400,-340,-28,-73,-329,-69,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,-300,-301,-280,147,-281,-282,-283,147,-334,-335,-336,-337,-287,147,147,30,456,-171,147,-154,-156,-151,-143,-144,-148,147,-146,-130,30,-177,-178,-221,-220,147,147,-237,147,147,147,147,147,-87,-74,147,-233,-234,-236,147,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,147,-12,147,-287,147,147,147,-308,-259,-260,-261,269,269,269,269,269,269,269,269,269,269,269,269,269,269,269,-295,-296,-297,-298,-299,-340,147,520,-172,-173,-153,-155,30,147,-222,147,-224,147,-86,-75,147,-232,-235,-340,-201,-278,-340,-68,147,-293,-294,147,-284,-285,543,-340,-28,-287,-223,147,147,147,147,147,147,-11,-287,147,147,-225,-87,-74,-227,-228,147,-302,-340,-309,147,147,147,-303,-226,-229,147,147,-231,-230,]),'TYPEID':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,62,63,64,67,68,69,70,71,72,73,74,75,76,77,78,80,81,82,90,91,96,97,98,99,100,101,102,103,104,106,112,113,114,115,116,117,118,119,121,122,123,124,125,126,127,128,129,134,137,140,141,192,193,194,196,197,203,204,205,206,207,208,209,210,211,212,213,214,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,289,290,294,298,299,304,311,312,313,318,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,466,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[35,35,-60,-62,-63,-64,-65,-66,-67,35,89,-70,-71,-52,-340,-340,-340,-128,-102,35,-340,-29,-107,-340,-125,-126,-127,-129,-241,119,123,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-157,-158,-61,35,-91,89,35,-129,-134,35,-98,-99,-100,-101,-104,89,-134,89,-90,-72,35,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-183,-27,-28,-185,-96,-169,-170,-338,-149,-150,35,35,35,-76,35,-92,89,35,-30,35,324,35,89,-184,-186,35,35,35,-152,-159,-339,89,-162,-163,-145,35,-147,35,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,35,-77,-73,-69,432,434,35,35,35,35,-35,-36,35,324,35,-171,35,-154,35,-156,-151,-160,-143,-144,-148,-146,-130,35,-177,-178,-221,-220,-237,-87,-84,35,-233,-234,-236,-31,-34,35,35,-172,-173,-153,-155,-161,89,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'ENUM':([0,2,4,5,6,7,8,9,10,11,14,15,19,21,22,23,26,27,28,29,34,50,51,52,53,54,55,56,57,58,59,60,64,67,68,70,71,72,73,90,91,96,97,98,99,100,101,102,103,112,116,117,121,124,125,126,127,128,129,137,140,141,193,197,203,204,205,207,208,210,211,213,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,333,335,338,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[36,36,-60,-62,-63,-64,-65,-66,-67,36,-70,-71,-52,-340,-340,-340,36,-340,-29,-107,-340,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,36,-91,36,-340,-134,36,-90,-72,36,-53,-93,-9,-10,-340,-94,-95,-97,-185,-96,-338,36,36,36,-76,36,-92,36,-30,36,36,-186,36,36,36,-159,-339,-162,-163,36,36,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,36,-77,-73,-69,36,36,36,36,-35,-36,36,36,36,36,-160,-130,36,-177,-178,-221,-220,-237,-87,-84,36,-233,-234,-236,-31,-34,36,36,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'VOID':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[38,38,-60,-62,-63,-64,-65,-66,-67,38,38,-70,-71,-52,-340,-340,-340,-128,-102,38,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,38,-91,38,38,-129,-134,38,-98,-99,-100,-101,-104,-134,-90,-72,38,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,38,38,38,-76,38,-92,38,-30,38,38,38,-186,38,38,38,-152,-159,-339,38,-162,-163,-145,38,-147,38,38,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,38,-77,-73,-69,38,38,38,38,-35,-36,38,38,-171,38,-154,38,-156,-151,-160,-143,-144,-148,-146,-130,38,-177,-178,-221,-220,-237,-87,-84,38,-233,-234,-236,-31,-34,38,38,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'_BOOL':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[39,39,-60,-62,-63,-64,-65,-66,-67,39,39,-70,-71,-52,-340,-340,-340,-128,-102,39,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,39,-91,39,39,-129,-134,39,-98,-99,-100,-101,-104,-134,-90,-72,39,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,39,39,39,-76,39,-92,39,-30,39,39,39,-186,39,39,39,-152,-159,-339,39,-162,-163,-145,39,-147,39,39,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,39,-77,-73,-69,39,39,39,39,-35,-36,39,39,-171,39,-154,39,-156,-151,-160,-143,-144,-148,-146,-130,39,-177,-178,-221,-220,-237,-87,-84,39,-233,-234,-236,-31,-34,39,39,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'CHAR':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[40,40,-60,-62,-63,-64,-65,-66,-67,40,40,-70,-71,-52,-340,-340,-340,-128,-102,40,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,40,-91,40,40,-129,-134,40,-98,-99,-100,-101,-104,-134,-90,-72,40,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,40,40,40,-76,40,-92,40,-30,40,40,40,-186,40,40,40,-152,-159,-339,40,-162,-163,-145,40,-147,40,40,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,40,-77,-73,-69,40,40,40,40,-35,-36,40,40,-171,40,-154,40,-156,-151,-160,-143,-144,-148,-146,-130,40,-177,-178,-221,-220,-237,-87,-84,40,-233,-234,-236,-31,-34,40,40,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'SHORT':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[41,41,-60,-62,-63,-64,-65,-66,-67,41,41,-70,-71,-52,-340,-340,-340,-128,-102,41,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,41,-91,41,41,-129,-134,41,-98,-99,-100,-101,-104,-134,-90,-72,41,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,41,41,41,-76,41,-92,41,-30,41,41,41,-186,41,41,41,-152,-159,-339,41,-162,-163,-145,41,-147,41,41,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,41,-77,-73,-69,41,41,41,41,-35,-36,41,41,-171,41,-154,41,-156,-151,-160,-143,-144,-148,-146,-130,41,-177,-178,-221,-220,-237,-87,-84,41,-233,-234,-236,-31,-34,41,41,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'INT':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[42,42,-60,-62,-63,-64,-65,-66,-67,42,42,-70,-71,-52,-340,-340,-340,-128,-102,42,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,42,-91,42,42,-129,-134,42,-98,-99,-100,-101,-104,-134,-90,-72,42,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,42,42,42,-76,42,-92,42,-30,42,42,42,-186,42,42,42,-152,-159,-339,42,-162,-163,-145,42,-147,42,42,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,42,-77,-73,-69,42,42,42,42,-35,-36,42,42,-171,42,-154,42,-156,-151,-160,-143,-144,-148,-146,-130,42,-177,-178,-221,-220,-237,-87,-84,42,-233,-234,-236,-31,-34,42,42,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'LONG':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[43,43,-60,-62,-63,-64,-65,-66,-67,43,43,-70,-71,-52,-340,-340,-340,-128,-102,43,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,43,-91,43,43,-129,-134,43,-98,-99,-100,-101,-104,-134,-90,-72,43,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,43,43,43,-76,43,-92,43,-30,43,43,43,-186,43,43,43,-152,-159,-339,43,-162,-163,-145,43,-147,43,43,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,43,-77,-73,-69,43,43,43,43,-35,-36,43,43,-171,43,-154,43,-156,-151,-160,-143,-144,-148,-146,-130,43,-177,-178,-221,-220,-237,-87,-84,43,-233,-234,-236,-31,-34,43,43,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'FLOAT':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[44,44,-60,-62,-63,-64,-65,-66,-67,44,44,-70,-71,-52,-340,-340,-340,-128,-102,44,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,44,-91,44,44,-129,-134,44,-98,-99,-100,-101,-104,-134,-90,-72,44,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,44,44,44,-76,44,-92,44,-30,44,44,44,-186,44,44,44,-152,-159,-339,44,-162,-163,-145,44,-147,44,44,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,44,-77,-73,-69,44,44,44,44,-35,-36,44,44,-171,44,-154,44,-156,-151,-160,-143,-144,-148,-146,-130,44,-177,-178,-221,-220,-237,-87,-84,44,-233,-234,-236,-31,-34,44,44,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'DOUBLE':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[45,45,-60,-62,-63,-64,-65,-66,-67,45,45,-70,-71,-52,-340,-340,-340,-128,-102,45,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,45,-91,45,45,-129,-134,45,-98,-99,-100,-101,-104,-134,-90,-72,45,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,45,45,45,-76,45,-92,45,-30,45,45,45,-186,45,45,45,-152,-159,-339,45,-162,-163,-145,45,-147,45,45,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,45,-77,-73,-69,45,45,45,45,-35,-36,45,45,-171,45,-154,45,-156,-151,-160,-143,-144,-148,-146,-130,45,-177,-178,-221,-220,-237,-87,-84,45,-233,-234,-236,-31,-34,45,45,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'_COMPLEX':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[46,46,-60,-62,-63,-64,-65,-66,-67,46,46,-70,-71,-52,-340,-340,-340,-128,-102,46,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,46,-91,46,46,-129,-134,46,-98,-99,-100,-101,-104,-134,-90,-72,46,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,46,46,46,-76,46,-92,46,-30,46,46,46,-186,46,46,46,-152,-159,-339,46,-162,-163,-145,46,-147,46,46,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,46,-77,-73,-69,46,46,46,46,-35,-36,46,46,-171,46,-154,46,-156,-151,-160,-143,-144,-148,-146,-130,46,-177,-178,-221,-220,-237,-87,-84,46,-233,-234,-236,-31,-34,46,46,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'SIGNED':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[47,47,-60,-62,-63,-64,-65,-66,-67,47,47,-70,-71,-52,-340,-340,-340,-128,-102,47,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,47,-91,47,47,-129,-134,47,-98,-99,-100,-101,-104,-134,-90,-72,47,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,47,47,47,-76,47,-92,47,-30,47,47,47,-186,47,47,47,-152,-159,-339,47,-162,-163,-145,47,-147,47,47,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,47,-77,-73,-69,47,47,47,47,-35,-36,47,47,-171,47,-154,47,-156,-151,-160,-143,-144,-148,-146,-130,47,-177,-178,-221,-220,-237,-87,-84,47,-233,-234,-236,-31,-34,47,47,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'UNSIGNED':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[48,48,-60,-62,-63,-64,-65,-66,-67,48,48,-70,-71,-52,-340,-340,-340,-128,-102,48,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,48,-91,48,48,-129,-134,48,-98,-99,-100,-101,-104,-134,-90,-72,48,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,48,48,48,-76,48,-92,48,-30,48,48,48,-186,48,48,48,-152,-159,-339,48,-162,-163,-145,48,-147,48,48,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,48,-77,-73,-69,48,48,48,48,-35,-36,48,48,-171,48,-154,48,-156,-151,-160,-143,-144,-148,-146,-130,48,-177,-178,-221,-220,-237,-87,-84,48,-233,-234,-236,-31,-34,48,48,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'__INT128':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,96,97,98,99,100,101,102,103,104,106,112,116,117,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[49,49,-60,-62,-63,-64,-65,-66,-67,49,49,-70,-71,-52,-340,-340,-340,-128,-102,49,-340,-29,-107,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,49,-91,49,49,-129,-134,49,-98,-99,-100,-101,-104,-134,-90,-72,49,-53,-93,-9,-10,-340,-94,-95,-103,-129,-97,-185,-96,-169,-170,-338,-149,-150,49,49,49,-76,49,-92,49,-30,49,49,49,-186,49,49,49,-152,-159,-339,49,-162,-163,-145,49,-147,49,49,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,49,-77,-73,-69,49,49,49,49,-35,-36,49,49,-171,49,-154,49,-156,-151,-160,-143,-144,-148,-146,-130,49,-177,-178,-221,-220,-237,-87,-84,49,-233,-234,-236,-31,-34,49,49,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'_ATOMIC':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,70,71,72,73,74,75,76,77,78,81,90,91,95,96,97,98,99,100,101,102,103,104,106,112,115,116,117,118,119,121,122,123,124,125,126,127,128,129,136,137,140,141,183,184,192,193,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,258,259,262,267,294,298,299,304,311,312,313,322,323,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,448,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,511,512,524,552,553,554,555,556,579,580,585,586,],[50,50,-60,-62,-63,-64,-65,-66,-67,72,81,-70,-71,-52,72,72,72,-128,-102,109,72,-29,-107,81,-125,-126,-127,72,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,72,-91,81,109,72,-134,72,-98,-99,-100,-101,-104,-134,-90,-72,81,50,-53,-93,-9,-10,72,-94,-95,-103,-129,-97,81,-185,-96,-169,-170,-338,-149,-150,50,50,50,-76,72,-92,81,50,-30,50,81,81,81,109,-186,50,50,50,-152,-159,-339,81,-162,-163,-145,72,-147,81,72,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,50,-77,81,81,-73,-69,50,50,50,50,-35,-36,50,50,81,-171,50,-154,50,-156,-151,-160,-143,-144,-148,-146,-130,50,-177,-178,-221,-220,-237,-87,-84,72,-233,-234,-236,-31,-34,81,50,50,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,81,81,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'CONST':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,95,96,97,101,104,106,115,116,118,119,121,122,123,124,125,126,127,128,129,136,137,140,141,183,184,192,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,258,259,262,267,294,298,299,304,311,312,313,322,323,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,448,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,511,512,524,552,553,554,555,556,579,580,585,586,],[51,51,-60,-62,-63,-64,-65,-66,-67,51,51,-70,-71,-52,51,51,51,-128,-102,51,-29,-107,51,-125,-126,-127,51,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,51,-91,51,51,-134,51,-98,-99,-100,-101,-104,-134,-90,-72,51,51,-53,51,-103,-129,51,-185,-169,-170,-338,-149,-150,51,51,51,-76,51,-92,51,51,-30,51,51,51,51,-186,51,51,51,-152,-159,-339,51,-162,-163,-145,51,-147,51,51,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,51,-77,51,51,-73,-69,51,51,51,51,-35,-36,51,51,51,-171,51,-154,51,-156,-151,-160,-143,-144,-148,-146,-130,51,-177,-178,-221,-220,-237,-87,-84,51,-233,-234,-236,-31,-34,51,51,51,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,51,51,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'RESTRICT':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,95,96,97,101,104,106,115,116,118,119,121,122,123,124,125,126,127,128,129,136,137,140,141,183,184,192,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,258,259,262,267,294,298,299,304,311,312,313,322,323,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,448,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,511,512,524,552,553,554,555,556,579,580,585,586,],[52,52,-60,-62,-63,-64,-65,-66,-67,52,52,-70,-71,-52,52,52,52,-128,-102,52,-29,-107,52,-125,-126,-127,52,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,52,-91,52,52,-134,52,-98,-99,-100,-101,-104,-134,-90,-72,52,52,-53,52,-103,-129,52,-185,-169,-170,-338,-149,-150,52,52,52,-76,52,-92,52,52,-30,52,52,52,52,-186,52,52,52,-152,-159,-339,52,-162,-163,-145,52,-147,52,52,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,52,-77,52,52,-73,-69,52,52,52,52,-35,-36,52,52,52,-171,52,-154,52,-156,-151,-160,-143,-144,-148,-146,-130,52,-177,-178,-221,-220,-237,-87,-84,52,-233,-234,-236,-31,-34,52,52,52,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,52,52,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'VOLATILE':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,95,96,97,101,104,106,115,116,118,119,121,122,123,124,125,126,127,128,129,136,137,140,141,183,184,192,197,203,204,205,206,207,208,209,210,211,212,213,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,258,259,262,267,294,298,299,304,311,312,313,322,323,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,448,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,511,512,524,552,553,554,555,556,579,580,585,586,],[53,53,-60,-62,-63,-64,-65,-66,-67,53,53,-70,-71,-52,53,53,53,-128,-102,53,-29,-107,53,-125,-126,-127,53,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,53,-91,53,53,-134,53,-98,-99,-100,-101,-104,-134,-90,-72,53,53,-53,53,-103,-129,53,-185,-169,-170,-338,-149,-150,53,53,53,-76,53,-92,53,53,-30,53,53,53,53,-186,53,53,53,-152,-159,-339,53,-162,-163,-145,53,-147,53,53,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,53,-77,53,53,-73,-69,53,53,53,53,-35,-36,53,53,53,-171,53,-154,53,-156,-151,-160,-143,-144,-148,-146,-130,53,-177,-178,-221,-220,-237,-87,-84,53,-233,-234,-236,-31,-34,53,53,53,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,53,53,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'AUTO':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[54,54,-60,-62,-63,-64,-65,-66,-67,54,54,-70,-71,-52,54,54,54,-128,-102,54,-29,-107,-125,-126,-127,54,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,54,-91,54,54,-134,54,-98,-99,-100,-101,-104,-134,-90,-72,54,-53,54,-103,-129,-169,-170,-338,-149,-150,-76,54,-92,54,-30,54,-152,-339,54,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,54,54,-171,-154,-156,-151,-130,54,-177,-178,-221,-220,-237,-87,-84,54,-233,-234,-236,-31,-34,54,54,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'REGISTER':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[55,55,-60,-62,-63,-64,-65,-66,-67,55,55,-70,-71,-52,55,55,55,-128,-102,55,-29,-107,-125,-126,-127,55,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,55,-91,55,55,-134,55,-98,-99,-100,-101,-104,-134,-90,-72,55,-53,55,-103,-129,-169,-170,-338,-149,-150,-76,55,-92,55,-30,55,-152,-339,55,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,55,55,-171,-154,-156,-151,-130,55,-177,-178,-221,-220,-237,-87,-84,55,-233,-234,-236,-31,-34,55,55,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'STATIC':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,95,96,97,101,104,106,116,118,119,121,122,123,127,128,129,136,137,140,184,192,197,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,259,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,448,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,512,524,552,553,554,555,556,579,580,585,586,],[29,29,-60,-62,-63,-64,-65,-66,-67,29,29,-70,-71,-52,29,29,29,-128,-102,29,-29,-107,-125,-126,-127,29,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,29,-91,29,29,-134,29,-98,-99,-100,-101,-104,-134,-90,-72,183,29,-53,29,-103,-129,-185,-169,-170,-338,-149,-150,-76,29,-92,258,29,-30,310,29,-186,-152,-339,29,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,402,-73,-69,-35,-36,29,29,-171,-154,-156,-151,-130,29,-177,-178,-221,-220,-237,-87,-84,29,-233,-234,-236,-31,-34,511,29,29,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,545,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'EXTERN':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[56,56,-60,-62,-63,-64,-65,-66,-67,56,56,-70,-71,-52,56,56,56,-128,-102,56,-29,-107,-125,-126,-127,56,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,56,-91,56,56,-134,56,-98,-99,-100,-101,-104,-134,-90,-72,56,-53,56,-103,-129,-169,-170,-338,-149,-150,-76,56,-92,56,-30,56,-152,-339,56,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,56,56,-171,-154,-156,-151,-130,56,-177,-178,-221,-220,-237,-87,-84,56,-233,-234,-236,-31,-34,56,56,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'TYPEDEF':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[57,57,-60,-62,-63,-64,-65,-66,-67,57,57,-70,-71,-52,57,57,57,-128,-102,57,-29,-107,-125,-126,-127,57,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,57,-91,57,57,-134,57,-98,-99,-100,-101,-104,-134,-90,-72,57,-53,57,-103,-129,-169,-170,-338,-149,-150,-76,57,-92,57,-30,57,-152,-339,57,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,57,57,-171,-154,-156,-151,-130,57,-177,-178,-221,-220,-237,-87,-84,57,-233,-234,-236,-31,-34,57,57,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'_THREAD_LOCAL':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[58,58,-60,-62,-63,-64,-65,-66,-67,58,58,-70,-71,-52,58,58,58,-128,-102,58,-29,-107,-125,-126,-127,58,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,58,-91,58,58,-134,58,-98,-99,-100,-101,-104,-134,-90,-72,58,-53,58,-103,-129,-169,-170,-338,-149,-150,-76,58,-92,58,-30,58,-152,-339,58,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,58,58,-171,-154,-156,-151,-130,58,-177,-178,-221,-220,-237,-87,-84,58,-233,-234,-236,-31,-34,58,58,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'INLINE':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[59,59,-60,-62,-63,-64,-65,-66,-67,59,59,-70,-71,-52,59,59,59,-128,-102,59,-29,-107,-125,-126,-127,59,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,59,-91,59,59,-134,59,-98,-99,-100,-101,-104,-134,-90,-72,59,-53,59,-103,-129,-169,-170,-338,-149,-150,-76,59,-92,59,-30,59,-152,-339,59,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,59,59,-171,-154,-156,-151,-130,59,-177,-178,-221,-220,-237,-87,-84,59,-233,-234,-236,-31,-34,59,59,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'_NORETURN':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,127,128,129,137,140,192,206,208,221,222,223,224,225,226,227,228,229,230,231,232,251,262,267,311,312,313,322,330,334,336,337,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[60,60,-60,-62,-63,-64,-65,-66,-67,60,60,-70,-71,-52,60,60,60,-128,-102,60,-29,-107,-125,-126,-127,60,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,60,-91,60,60,-134,60,-98,-99,-100,-101,-104,-134,-90,-72,60,-53,60,-103,-129,-169,-170,-338,-149,-150,-76,60,-92,60,-30,60,-152,-339,60,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-77,-73,-69,-35,-36,60,60,-171,-154,-156,-151,-130,60,-177,-178,-221,-220,-237,-87,-84,60,-233,-234,-236,-31,-34,60,60,-172,-173,-153,-155,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'_ALIGNAS':([0,2,4,5,6,7,8,9,10,11,12,14,15,19,21,22,23,24,25,27,28,29,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,64,67,68,69,71,72,73,74,75,76,77,78,81,90,91,96,97,101,104,106,118,119,121,122,123,124,125,126,127,128,129,137,140,141,192,203,204,205,206,207,208,209,210,211,212,214,216,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,330,333,334,335,336,337,338,340,341,342,348,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,459,460,463,464,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[61,61,-60,-62,-63,-64,-65,-66,-67,61,61,-70,-71,-52,61,61,61,-128,-102,61,-29,-107,-125,-126,-127,61,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,61,-91,61,61,-134,61,-98,-99,-100,-101,-104,-134,-90,-72,61,-53,61,-103,-129,-169,-170,-338,-149,-150,61,61,61,-76,61,-92,61,-30,61,61,61,61,61,-152,-159,-339,61,-162,-163,-145,-147,61,61,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,61,-77,-73,-69,61,61,61,61,-35,-36,61,61,-171,61,-154,61,-156,-151,-160,-143,-144,-148,-146,-130,61,-177,-178,-221,-220,-237,-87,-84,61,-233,-234,-236,-31,-34,61,61,-172,-173,-153,-155,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'STRUCT':([0,2,4,5,6,7,8,9,10,11,14,15,19,21,22,23,26,27,28,29,34,50,51,52,53,54,55,56,57,58,59,60,64,67,68,70,71,72,73,90,91,96,97,98,99,100,101,102,103,112,116,117,121,124,125,126,127,128,129,137,140,141,193,197,203,204,205,207,208,210,211,213,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,333,335,338,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[62,62,-60,-62,-63,-64,-65,-66,-67,62,-70,-71,-52,-340,-340,-340,62,-340,-29,-107,-340,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,62,-91,62,-340,-134,62,-90,-72,62,-53,-93,-9,-10,-340,-94,-95,-97,-185,-96,-338,62,62,62,-76,62,-92,62,-30,62,62,-186,62,62,62,-159,-339,-162,-163,62,62,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,62,-77,-73,-69,62,62,62,62,-35,-36,62,62,62,62,-160,-130,62,-177,-178,-221,-220,-237,-87,-84,62,-233,-234,-236,-31,-34,62,62,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'UNION':([0,2,4,5,6,7,8,9,10,11,14,15,19,21,22,23,26,27,28,29,34,50,51,52,53,54,55,56,57,58,59,60,64,67,68,70,71,72,73,90,91,96,97,98,99,100,101,102,103,112,116,117,121,124,125,126,127,128,129,137,140,141,193,197,203,204,205,207,208,210,211,213,221,222,223,224,225,226,227,228,229,230,231,232,238,251,262,267,294,298,299,304,311,312,313,322,333,335,338,349,351,353,354,355,356,361,370,371,372,374,375,377,439,440,449,458,465,469,471,479,480,483,484,499,508,509,524,552,553,554,555,556,579,580,585,586,],[63,63,-60,-62,-63,-64,-65,-66,-67,63,-70,-71,-52,-340,-340,-340,63,-340,-29,-107,-340,-134,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-61,63,-91,63,-340,-134,63,-90,-72,63,-53,-93,-9,-10,-340,-94,-95,-97,-185,-96,-338,63,63,63,-76,63,-92,63,-30,63,63,-186,63,63,63,-159,-339,-162,-163,63,63,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,63,-77,-73,-69,63,63,63,63,-35,-36,63,63,63,63,-160,-130,63,-177,-178,-221,-220,-237,-87,-84,63,-233,-234,-236,-31,-34,63,63,-161,-222,-224,-86,-84,-232,-235,-68,-32,-33,-223,-225,-87,-84,-227,-228,-226,-229,-231,-230,]),'LBRACE':([11,15,19,28,36,37,62,63,65,66,67,68,73,90,91,97,118,119,121,122,123,128,129,131,135,140,195,208,221,222,223,224,225,226,227,228,229,230,231,232,238,242,256,262,267,311,312,355,356,358,360,361,369,370,371,374,375,377,392,393,394,405,439,440,469,470,471,474,479,480,483,484,487,489,498,499,504,505,508,509,524,525,526,527,532,533,552,553,554,555,556,562,570,579,580,582,584,585,586,],[-340,-71,-52,-29,121,121,-157,-158,121,-7,-8,-91,-340,-90,-72,-53,121,121,-338,121,121,121,-92,121,121,-30,121,-339,121,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,121,121,-340,-73,-69,-35,-36,-221,-220,121,121,-237,121,-87,-74,-233,-234,-236,-11,121,-12,121,-31,-34,-222,121,-224,121,-86,-75,-232,-235,-340,-201,-340,-68,121,121,-32,-33,-223,121,121,121,121,-11,-225,-87,-74,-227,-228,-340,121,-226,-229,121,121,-231,-230,]),'RBRACE':([15,90,91,121,124,128,139,143,144,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,200,201,202,203,204,205,207,208,210,211,219,220,221,222,223,224,225,226,227,228,229,230,231,232,249,250,255,256,262,263,267,291,292,293,295,296,297,300,301,302,303,328,329,331,333,335,338,355,356,361,370,371,374,375,377,390,391,392,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,461,462,465,469,471,473,479,480,483,484,485,486,487,488,497,499,501,502,505,506,524,531,537,538,552,553,554,555,556,560,561,562,563,574,579,580,585,586,],[-71,-90,-72,-338,208,-340,-328,-306,-255,-256,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,208,-174,-179,208,208,208,-159,-339,-162,-163,208,-5,-6,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-242,-277,-196,-340,-73,-329,-69,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,208,208,-175,208,208,-160,-221,-220,-237,-87,-84,-233,-234,-236,208,-22,-21,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,-295,-296,-297,-298,-299,-176,-180,-161,-222,-224,-240,-86,-84,-232,-235,-243,-197,208,-199,-278,-68,-293,-294,-284,-285,-223,-198,208,-257,-225,-87,-84,-227,-228,-200,-302,208,-309,-303,-226,-229,-231,-230,]),'CASE':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,234,-339,234,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,234,-73,-69,-221,-220,234,234,-237,234,-87,-74,-233,-234,-236,-222,234,-224,-86,-75,-232,-235,-68,-223,234,234,234,-225,-87,-74,-227,-228,234,-226,-229,234,234,-231,-230,]),'DEFAULT':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,235,-339,235,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,235,-73,-69,-221,-220,235,235,-237,235,-87,-74,-233,-234,-236,-222,235,-224,-86,-75,-232,-235,-68,-223,235,235,235,-225,-87,-74,-227,-228,235,-226,-229,235,235,-231,-230,]),'IF':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,237,-339,237,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,237,-73,-69,-221,-220,237,237,-237,237,-87,-74,-233,-234,-236,-222,237,-224,-86,-75,-232,-235,-68,-223,237,237,237,-225,-87,-74,-227,-228,237,-226,-229,237,237,-231,-230,]),'SWITCH':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,240,-339,240,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,240,-73,-69,-221,-220,240,240,-237,240,-87,-74,-233,-234,-236,-222,240,-224,-86,-75,-232,-235,-68,-223,240,240,240,-225,-87,-74,-227,-228,240,-226,-229,240,240,-231,-230,]),'WHILE':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,368,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,241,-339,241,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,241,-73,-69,-221,-220,241,241,-237,478,241,-87,-74,-233,-234,-236,-222,241,-224,-86,-75,-232,-235,-68,-223,241,241,241,-225,-87,-74,-227,-228,241,-226,-229,241,241,-231,-230,]),'DO':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,242,-339,242,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,242,-73,-69,-221,-220,242,242,-237,242,-87,-74,-233,-234,-236,-222,242,-224,-86,-75,-232,-235,-68,-223,242,242,242,-225,-87,-74,-227,-228,242,-226,-229,242,242,-231,-230,]),'FOR':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,243,-339,243,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,243,-73,-69,-221,-220,243,243,-237,243,-87,-74,-233,-234,-236,-222,243,-224,-86,-75,-232,-235,-68,-223,243,243,243,-225,-87,-74,-227,-228,243,-226,-229,243,243,-231,-230,]),'GOTO':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,244,-339,244,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,244,-73,-69,-221,-220,244,244,-237,244,-87,-74,-233,-234,-236,-222,244,-224,-86,-75,-232,-235,-68,-223,244,244,244,-225,-87,-74,-227,-228,244,-226,-229,244,244,-231,-230,]),'BREAK':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,245,-339,245,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,245,-73,-69,-221,-220,245,245,-237,245,-87,-74,-233,-234,-236,-222,245,-224,-86,-75,-232,-235,-68,-223,245,245,245,-225,-87,-74,-227,-228,245,-226,-229,245,245,-231,-230,]),'CONTINUE':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,246,-339,246,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,246,-73,-69,-221,-220,246,246,-237,246,-87,-74,-233,-234,-236,-222,246,-224,-86,-75,-232,-235,-68,-223,246,246,246,-225,-87,-74,-227,-228,246,-226,-229,246,246,-231,-230,]),'RETURN':([15,90,91,121,128,208,221,222,223,224,225,226,227,228,229,230,231,232,242,262,267,355,356,358,360,361,369,370,371,374,375,377,469,470,471,479,480,483,484,499,524,525,526,527,552,553,554,555,556,570,579,580,582,584,585,586,],[-71,-90,-72,-338,247,-339,247,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,247,-73,-69,-221,-220,247,247,-237,247,-87,-74,-233,-234,-236,-222,247,-224,-86,-75,-232,-235,-68,-223,247,247,247,-225,-87,-74,-227,-228,247,-226,-229,247,247,-231,-230,]),'PLUSPLUS':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,139,141,143,147,148,149,150,152,153,154,155,156,158,159,160,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,233,234,238,242,247,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,294,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,406,429,431,432,433,434,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,501,502,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,561,562,563,565,570,572,574,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,153,-340,-27,-28,-185,-338,153,153,153,-340,-328,153,-306,-287,-288,-289,-286,291,153,153,153,153,-292,-315,-290,-291,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,153,-340,-28,153,-186,-339,153,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,153,153,153,153,-340,153,-340,-28,-73,-329,-69,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,-300,-301,153,153,-334,-335,-336,-337,-287,153,153,-340,153,153,-221,-220,153,153,-237,153,153,153,153,153,-87,-74,153,-233,-234,-236,153,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,153,-12,153,-287,153,153,153,-308,-295,-296,-297,-298,-299,-340,153,153,153,-222,153,-224,153,-86,-75,153,-232,-235,-340,-201,-340,-68,153,-293,-294,153,153,-340,-28,-287,-223,153,153,153,153,153,153,-11,-287,153,153,-225,-87,-74,-227,-228,153,-302,-340,-309,153,153,153,-303,-226,-229,153,153,-231,-230,]),'MINUSMINUS':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,139,141,143,147,148,149,150,152,153,154,155,156,158,159,160,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,233,234,238,242,247,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,294,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,406,429,431,432,433,434,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,501,502,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,561,562,563,565,570,572,574,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,154,-340,-27,-28,-185,-338,154,154,154,-340,-328,154,-306,-287,-288,-289,-286,292,154,154,154,154,-292,-315,-290,-291,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,154,-340,-28,154,-186,-339,154,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,154,154,154,154,-340,154,-340,-28,-73,-329,-69,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,-300,-301,154,154,-334,-335,-336,-337,-287,154,154,-340,154,154,-221,-220,154,154,-237,154,154,154,154,154,-87,-74,154,-233,-234,-236,154,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,154,-12,154,-287,154,154,154,-308,-295,-296,-297,-298,-299,-340,154,154,154,-222,154,-224,154,-86,-75,154,-232,-235,-340,-201,-340,-68,154,-293,-294,154,154,-340,-28,-287,-223,154,154,154,154,154,154,-11,-287,154,154,-225,-87,-74,-227,-228,154,-302,-340,-309,154,154,154,-303,-226,-229,154,154,-231,-230,]),'SIZEOF':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,156,-340,-27,-28,-185,-338,156,156,156,-340,156,-287,-288,-289,-286,156,156,156,156,-290,-291,156,-340,-28,156,-186,-339,156,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,156,156,156,156,-340,156,-340,-28,-73,-69,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,-287,156,156,-340,156,156,-221,-220,156,156,-237,156,156,156,156,156,-87,-74,156,-233,-234,-236,156,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,156,-12,156,-287,156,156,156,-340,156,156,156,-222,156,-224,156,-86,-75,156,-232,-235,-340,-201,-340,-68,156,156,156,-340,-28,-287,-223,156,156,156,156,156,156,-11,-287,156,156,-225,-87,-74,-227,-228,156,-340,156,156,156,-226,-229,156,156,-231,-230,]),'_ALIGNOF':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,157,-340,-27,-28,-185,-338,157,157,157,-340,157,-287,-288,-289,-286,157,157,157,157,-290,-291,157,-340,-28,157,-186,-339,157,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,157,157,157,157,-340,157,-340,-28,-73,-69,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,-287,157,157,-340,157,157,-221,-220,157,157,-237,157,157,157,157,157,-87,-74,157,-233,-234,-236,157,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,157,-12,157,-287,157,157,157,-340,157,157,157,-222,157,-224,157,-86,-75,157,-232,-235,-340,-201,-340,-68,157,157,157,-340,-28,-287,-223,157,157,157,157,157,157,-11,-287,157,157,-225,-87,-74,-227,-228,157,-340,157,157,157,-226,-229,157,157,-231,-230,]),'AND':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,139,141,143,145,146,147,148,149,150,151,152,153,154,155,156,158,159,160,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,233,234,238,242,247,250,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,293,294,295,296,297,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,497,498,499,500,501,502,503,505,506,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,561,562,563,565,570,572,574,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,150,-340,-27,-28,-185,-338,150,150,150,-340,-328,150,-306,282,-258,-287,-288,-289,-286,-277,-279,150,150,150,150,-292,-315,-290,-291,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,150,-340,-28,150,-186,-339,150,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,150,150,150,150,-277,-340,150,-340,-28,-73,-329,-69,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,-300,-301,-280,150,-281,-282,-283,150,-334,-335,-336,-337,-287,150,150,-340,150,150,-221,-220,150,150,-237,150,150,150,150,150,-87,-74,150,-233,-234,-236,150,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,150,-12,150,-287,150,150,150,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,282,282,282,282,-295,-296,-297,-298,-299,-340,150,150,150,-222,150,-224,150,-86,-75,150,-232,-235,-340,-201,-278,-340,-68,150,-293,-294,150,-284,-285,150,-340,-28,-287,-223,150,150,150,150,150,150,-11,-287,150,150,-225,-87,-74,-227,-228,150,-302,-340,-309,150,150,150,-303,-226,-229,150,150,-231,-230,]),'PLUS':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,139,141,143,145,146,147,148,149,150,151,152,153,154,155,156,158,159,160,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,233,234,238,242,247,250,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,293,294,295,296,297,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,497,498,499,500,501,502,503,505,506,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,561,562,563,565,570,572,574,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,148,-340,-27,-28,-185,-338,148,148,148,-340,-328,148,-306,272,-258,-287,-288,-289,-286,-277,-279,148,148,148,148,-292,-315,-290,-291,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,148,-340,-28,148,-186,-339,148,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,148,148,148,148,-277,-340,148,-340,-28,-73,-329,-69,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,-300,-301,-280,148,-281,-282,-283,148,-334,-335,-336,-337,-287,148,148,-340,148,148,-221,-220,148,148,-237,148,148,148,148,148,-87,-74,148,-233,-234,-236,148,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,148,-12,148,-287,148,148,148,-308,-259,-260,-261,-262,-263,272,272,272,272,272,272,272,272,272,272,272,272,272,-295,-296,-297,-298,-299,-340,148,148,148,-222,148,-224,148,-86,-75,148,-232,-235,-340,-201,-278,-340,-68,148,-293,-294,148,-284,-285,148,-340,-28,-287,-223,148,148,148,148,148,148,-11,-287,148,148,-225,-87,-74,-227,-228,148,-302,-340,-309,148,148,148,-303,-226,-229,148,148,-231,-230,]),'MINUS':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,139,141,143,145,146,147,148,149,150,151,152,153,154,155,156,158,159,160,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,233,234,238,242,247,250,256,257,258,259,262,263,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,291,292,293,294,295,296,297,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,497,498,499,500,501,502,503,505,506,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,561,562,563,565,570,572,574,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,149,-340,-27,-28,-185,-338,149,149,149,-340,-328,149,-306,273,-258,-287,-288,-289,-286,-277,-279,149,149,149,149,-292,-315,-290,-291,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,149,-340,-28,149,-186,-339,149,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,-315,149,149,149,149,-277,-340,149,-340,-28,-73,-329,-69,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,-300,-301,-280,149,-281,-282,-283,149,-334,-335,-336,-337,-287,149,149,-340,149,149,-221,-220,149,149,-237,149,149,149,149,149,-87,-74,149,-233,-234,-236,149,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,149,-12,149,-287,149,149,149,-308,-259,-260,-261,-262,-263,273,273,273,273,273,273,273,273,273,273,273,273,273,-295,-296,-297,-298,-299,-340,149,149,149,-222,149,-224,149,-86,-75,149,-232,-235,-340,-201,-278,-340,-68,149,-293,-294,149,-284,-285,149,-340,-28,-287,-223,149,149,149,149,149,149,-11,-287,149,149,-225,-87,-74,-227,-228,149,-302,-340,-309,149,149,149,-303,-226,-229,149,149,-231,-230,]),'NOT':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,160,-340,-27,-28,-185,-338,160,160,160,-340,160,-287,-288,-289,-286,160,160,160,160,-290,-291,160,-340,-28,160,-186,-339,160,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,160,160,160,160,-340,160,-340,-28,-73,-69,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,-287,160,160,-340,160,160,-221,-220,160,160,-237,160,160,160,160,160,-87,-74,160,-233,-234,-236,160,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,160,-12,160,-287,160,160,160,-340,160,160,160,-222,160,-224,160,-86,-75,160,-232,-235,-340,-201,-340,-68,160,160,160,-340,-28,-287,-223,160,160,160,160,160,160,-11,-287,160,160,-225,-87,-74,-227,-228,160,-340,160,160,160,-226,-229,160,160,-231,-230,]),'LNOT':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,161,-340,-27,-28,-185,-338,161,161,161,-340,161,-287,-288,-289,-286,161,161,161,161,-290,-291,161,-340,-28,161,-186,-339,161,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,161,161,161,161,-340,161,-340,-28,-73,-69,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,-287,161,161,-340,161,161,-221,-220,161,161,-237,161,161,161,161,161,-87,-74,161,-233,-234,-236,161,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,161,-12,161,-287,161,161,161,-340,161,161,161,-222,161,-224,161,-86,-75,161,-232,-235,-340,-201,-340,-68,161,161,161,-340,-28,-287,-223,161,161,161,161,161,161,-11,-287,161,161,-225,-87,-74,-227,-228,161,-340,161,161,161,-226,-229,161,161,-231,-230,]),'OFFSETOF':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,165,-340,-27,-28,-185,-338,165,165,165,-340,165,-287,-288,-289,-286,165,165,165,165,-290,-291,165,-340,-28,165,-186,-339,165,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,165,165,165,165,-340,165,-340,-28,-73,-69,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,-287,165,165,-340,165,165,-221,-220,165,165,-237,165,165,165,165,165,-87,-74,165,-233,-234,-236,165,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,165,-12,165,-287,165,165,165,-340,165,165,165,-222,165,-224,165,-86,-75,165,-232,-235,-340,-201,-340,-68,165,165,165,-340,-28,-287,-223,165,165,165,165,165,165,-11,-287,165,165,-225,-87,-74,-227,-228,165,-340,165,165,165,-226,-229,165,165,-231,-230,]),'INT_CONST_DEC':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,166,-340,-27,-28,-185,-338,166,166,166,-340,166,-287,-288,-289,-286,166,166,166,166,-290,-291,166,-340,-28,166,-186,-339,166,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,166,166,166,166,-340,166,-340,-28,-73,-69,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,-287,166,166,-340,166,166,-221,-220,166,166,-237,166,166,166,166,166,-87,-74,166,-233,-234,-236,166,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,166,-12,166,-287,166,166,166,-340,166,166,166,-222,166,-224,166,-86,-75,166,-232,-235,-340,-201,-340,-68,166,166,166,-340,-28,-287,-223,166,166,166,166,166,166,-11,-287,166,166,-225,-87,-74,-227,-228,166,-340,166,166,166,-226,-229,166,166,-231,-230,]),'INT_CONST_OCT':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,167,-340,-27,-28,-185,-338,167,167,167,-340,167,-287,-288,-289,-286,167,167,167,167,-290,-291,167,-340,-28,167,-186,-339,167,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,167,167,167,167,-340,167,-340,-28,-73,-69,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,-287,167,167,-340,167,167,-221,-220,167,167,-237,167,167,167,167,167,-87,-74,167,-233,-234,-236,167,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,167,-12,167,-287,167,167,167,-340,167,167,167,-222,167,-224,167,-86,-75,167,-232,-235,-340,-201,-340,-68,167,167,167,-340,-28,-287,-223,167,167,167,167,167,167,-11,-287,167,167,-225,-87,-74,-227,-228,167,-340,167,167,167,-226,-229,167,167,-231,-230,]),'INT_CONST_HEX':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,168,-340,-27,-28,-185,-338,168,168,168,-340,168,-287,-288,-289,-286,168,168,168,168,-290,-291,168,-340,-28,168,-186,-339,168,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,168,168,168,168,-340,168,-340,-28,-73,-69,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,-287,168,168,-340,168,168,-221,-220,168,168,-237,168,168,168,168,168,-87,-74,168,-233,-234,-236,168,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,168,-12,168,-287,168,168,168,-340,168,168,168,-222,168,-224,168,-86,-75,168,-232,-235,-340,-201,-340,-68,168,168,168,-340,-28,-287,-223,168,168,168,168,168,168,-11,-287,168,168,-225,-87,-74,-227,-228,168,-340,168,168,168,-226,-229,168,168,-231,-230,]),'INT_CONST_BIN':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,169,-340,-27,-28,-185,-338,169,169,169,-340,169,-287,-288,-289,-286,169,169,169,169,-290,-291,169,-340,-28,169,-186,-339,169,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,169,169,169,169,-340,169,-340,-28,-73,-69,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,-287,169,169,-340,169,169,-221,-220,169,169,-237,169,169,169,169,169,-87,-74,169,-233,-234,-236,169,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,169,-12,169,-287,169,169,169,-340,169,169,169,-222,169,-224,169,-86,-75,169,-232,-235,-340,-201,-340,-68,169,169,169,-340,-28,-287,-223,169,169,169,169,169,169,-11,-287,169,169,-225,-87,-74,-227,-228,169,-340,169,169,169,-226,-229,169,169,-231,-230,]),'INT_CONST_CHAR':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,170,-340,-27,-28,-185,-338,170,170,170,-340,170,-287,-288,-289,-286,170,170,170,170,-290,-291,170,-340,-28,170,-186,-339,170,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,170,170,170,170,-340,170,-340,-28,-73,-69,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,-287,170,170,-340,170,170,-221,-220,170,170,-237,170,170,170,170,170,-87,-74,170,-233,-234,-236,170,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,170,-12,170,-287,170,170,170,-340,170,170,170,-222,170,-224,170,-86,-75,170,-232,-235,-340,-201,-340,-68,170,170,170,-340,-28,-287,-223,170,170,170,170,170,170,-11,-287,170,170,-225,-87,-74,-227,-228,170,-340,170,170,170,-226,-229,170,170,-231,-230,]),'FLOAT_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,171,-340,-27,-28,-185,-338,171,171,171,-340,171,-287,-288,-289,-286,171,171,171,171,-290,-291,171,-340,-28,171,-186,-339,171,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,171,171,171,171,-340,171,-340,-28,-73,-69,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,-287,171,171,-340,171,171,-221,-220,171,171,-237,171,171,171,171,171,-87,-74,171,-233,-234,-236,171,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,171,-12,171,-287,171,171,171,-340,171,171,171,-222,171,-224,171,-86,-75,171,-232,-235,-340,-201,-340,-68,171,171,171,-340,-28,-287,-223,171,171,171,171,171,171,-11,-287,171,171,-225,-87,-74,-227,-228,171,-340,171,171,171,-226,-229,171,171,-231,-230,]),'HEX_FLOAT_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,172,-340,-27,-28,-185,-338,172,172,172,-340,172,-287,-288,-289,-286,172,172,172,172,-290,-291,172,-340,-28,172,-186,-339,172,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,172,172,172,172,-340,172,-340,-28,-73,-69,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,-287,172,172,-340,172,172,-221,-220,172,172,-237,172,172,172,172,172,-87,-74,172,-233,-234,-236,172,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,172,-12,172,-287,172,172,172,-340,172,172,172,-222,172,-224,172,-86,-75,172,-232,-235,-340,-201,-340,-68,172,172,172,-340,-28,-287,-223,172,172,172,172,172,172,-11,-287,172,172,-225,-87,-74,-227,-228,172,-340,172,172,172,-226,-229,172,172,-231,-230,]),'CHAR_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,173,-340,-27,-28,-185,-338,173,173,173,-340,173,-287,-288,-289,-286,173,173,173,173,-290,-291,173,-340,-28,173,-186,-339,173,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,173,173,173,173,-340,173,-340,-28,-73,-69,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,-287,173,173,-340,173,173,-221,-220,173,173,-237,173,173,173,173,173,-87,-74,173,-233,-234,-236,173,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,173,-12,173,-287,173,173,173,-340,173,173,173,-222,173,-224,173,-86,-75,173,-232,-235,-340,-201,-340,-68,173,173,173,-340,-28,-287,-223,173,173,173,173,173,173,-11,-287,173,173,-225,-87,-74,-227,-228,173,-340,173,173,173,-226,-229,173,173,-231,-230,]),'WCHAR_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,174,-340,-27,-28,-185,-338,174,174,174,-340,174,-287,-288,-289,-286,174,174,174,174,-290,-291,174,-340,-28,174,-186,-339,174,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,174,174,174,174,-340,174,-340,-28,-73,-69,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,-287,174,174,-340,174,174,-221,-220,174,174,-237,174,174,174,174,174,-87,-74,174,-233,-234,-236,174,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,174,-12,174,-287,174,174,174,-340,174,174,174,-222,174,-224,174,-86,-75,174,-232,-235,-340,-201,-340,-68,174,174,174,-340,-28,-287,-223,174,174,174,174,174,174,-11,-287,174,174,-225,-87,-74,-227,-228,174,-340,174,174,174,-226,-229,174,174,-231,-230,]),'U8CHAR_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,175,-340,-27,-28,-185,-338,175,175,175,-340,175,-287,-288,-289,-286,175,175,175,175,-290,-291,175,-340,-28,175,-186,-339,175,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,175,175,175,175,-340,175,-340,-28,-73,-69,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,-287,175,175,-340,175,175,-221,-220,175,175,-237,175,175,175,175,175,-87,-74,175,-233,-234,-236,175,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,175,-12,175,-287,175,175,175,-340,175,175,175,-222,175,-224,175,-86,-75,175,-232,-235,-340,-201,-340,-68,175,175,175,-340,-28,-287,-223,175,175,175,175,175,175,-11,-287,175,175,-225,-87,-74,-227,-228,175,-340,175,175,175,-226,-229,175,175,-231,-230,]),'U16CHAR_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,176,-340,-27,-28,-185,-338,176,176,176,-340,176,-287,-288,-289,-286,176,176,176,176,-290,-291,176,-340,-28,176,-186,-339,176,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,176,176,176,176,-340,176,-340,-28,-73,-69,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,-287,176,176,-340,176,176,-221,-220,176,176,-237,176,176,176,176,176,-87,-74,176,-233,-234,-236,176,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,176,-12,176,-287,176,176,176,-340,176,176,176,-222,176,-224,176,-86,-75,176,-232,-235,-340,-201,-340,-68,176,176,176,-340,-28,-287,-223,176,176,176,176,176,176,-11,-287,176,176,-225,-87,-74,-227,-228,176,-340,176,176,176,-226,-229,176,176,-231,-230,]),'U32CHAR_CONST':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,177,-340,-27,-28,-185,-338,177,177,177,-340,177,-287,-288,-289,-286,177,177,177,177,-290,-291,177,-340,-28,177,-186,-339,177,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,177,177,177,177,-340,177,-340,-28,-73,-69,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,-287,177,177,-340,177,177,-221,-220,177,177,-237,177,177,177,177,177,-87,-74,177,-233,-234,-236,177,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,177,-12,177,-287,177,177,177,-340,177,177,177,-222,177,-224,177,-86,-75,177,-232,-235,-340,-201,-340,-68,177,177,177,-340,-28,-287,-223,177,177,177,177,177,177,-11,-287,177,177,-225,-87,-74,-227,-228,177,-340,177,177,177,-226,-229,177,177,-231,-230,]),'STRING_LITERAL':([15,51,52,53,81,90,91,92,94,95,114,115,116,121,126,128,135,136,138,139,141,143,147,148,149,150,153,154,155,156,160,161,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,263,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,407,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,139,139,-340,-27,-28,-185,-338,139,139,139,-340,263,-328,139,263,-287,-288,-289,-286,139,139,139,139,-290,-291,139,-340,-28,139,-186,-339,139,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,139,139,139,139,-340,139,-340,-28,-73,-329,139,-69,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,-287,139,139,-340,139,139,-221,-220,139,139,-237,139,139,139,139,139,-87,-74,139,-233,-234,-236,139,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,139,-12,139,-287,139,139,139,263,-340,139,139,139,-222,139,-224,139,-86,-75,139,-232,-235,-340,-201,-340,-68,139,139,139,-340,-28,-287,-223,139,139,139,139,139,139,-11,-287,139,139,-225,-87,-74,-227,-228,139,-340,139,139,139,-226,-229,139,139,-231,-230,]),'WSTRING_LITERAL':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,164,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,178,-340,-27,-28,-185,-338,178,178,178,-340,178,-287,-288,-289,-286,178,178,178,178,-290,-291,300,-330,-331,-332,-333,178,-340,-28,178,-186,-339,178,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,178,178,178,178,-340,178,-340,-28,-73,-69,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,-334,-335,-336,-337,-287,178,178,-340,178,178,-221,-220,178,178,-237,178,178,178,178,178,-87,-74,178,-233,-234,-236,178,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,178,-12,178,-287,178,178,178,-340,178,178,178,-222,178,-224,178,-86,-75,178,-232,-235,-340,-201,-340,-68,178,178,178,-340,-28,-287,-223,178,178,178,178,178,178,-11,-287,178,178,-225,-87,-74,-227,-228,178,-340,178,178,178,-226,-229,178,178,-231,-230,]),'U8STRING_LITERAL':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,164,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,179,-340,-27,-28,-185,-338,179,179,179,-340,179,-287,-288,-289,-286,179,179,179,179,-290,-291,301,-330,-331,-332,-333,179,-340,-28,179,-186,-339,179,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,179,179,179,179,-340,179,-340,-28,-73,-69,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,-334,-335,-336,-337,-287,179,179,-340,179,179,-221,-220,179,179,-237,179,179,179,179,179,-87,-74,179,-233,-234,-236,179,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,179,-12,179,-287,179,179,179,-340,179,179,179,-222,179,-224,179,-86,-75,179,-232,-235,-340,-201,-340,-68,179,179,179,-340,-28,-287,-223,179,179,179,179,179,179,-11,-287,179,179,-225,-87,-74,-227,-228,179,-340,179,179,179,-226,-229,179,179,-231,-230,]),'U16STRING_LITERAL':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,164,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,180,-340,-27,-28,-185,-338,180,180,180,-340,180,-287,-288,-289,-286,180,180,180,180,-290,-291,302,-330,-331,-332,-333,180,-340,-28,180,-186,-339,180,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,180,180,180,180,-340,180,-340,-28,-73,-69,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,-334,-335,-336,-337,-287,180,180,-340,180,180,-221,-220,180,180,-237,180,180,180,180,180,-87,-74,180,-233,-234,-236,180,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,180,-12,180,-287,180,180,180,-340,180,180,180,-222,180,-224,180,-86,-75,180,-232,-235,-340,-201,-340,-68,180,180,180,-340,-28,-287,-223,180,180,180,180,180,180,-11,-287,180,180,-225,-87,-74,-227,-228,180,-340,180,180,180,-226,-229,180,180,-231,-230,]),'U32STRING_LITERAL':([15,51,52,53,81,90,91,94,95,114,115,116,121,126,128,135,136,141,147,148,149,150,153,154,155,156,160,161,164,178,179,180,181,182,183,184,195,197,208,221,222,223,224,225,226,227,228,229,230,231,232,234,238,242,247,256,257,258,259,262,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,300,301,302,303,306,309,310,323,332,347,355,356,358,360,361,362,365,366,367,369,370,371,372,374,375,377,378,379,380,381,382,383,384,385,386,387,388,389,392,393,394,397,400,401,402,405,448,455,457,467,469,470,471,474,479,480,482,483,484,487,489,498,499,500,503,510,511,512,520,524,525,526,527,528,529,532,533,543,544,545,552,553,554,555,556,559,562,565,570,572,579,580,582,584,585,586,],[-71,-131,-132,-133,-134,-90,-72,181,-340,-27,-28,-185,-338,181,181,181,-340,181,-287,-288,-289,-286,181,181,181,181,-290,-291,303,-330,-331,-332,-333,181,-340,-28,181,-186,-339,181,-219,-217,-218,-78,-79,-80,-81,-82,-83,-84,-85,181,181,181,181,-340,181,-340,-28,-73,-69,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,-334,-335,-336,-337,-287,181,181,-340,181,181,-221,-220,181,181,-237,181,181,181,181,181,-87,-74,181,-233,-234,-236,181,-244,-245,-246,-247,-248,-249,-250,-251,-252,-253,-254,-11,181,-12,181,-287,181,181,181,-340,181,181,181,-222,181,-224,181,-86,-75,181,-232,-235,-340,-201,-340,-68,181,181,181,-340,-28,-287,-223,181,181,181,181,181,181,-11,-287,181,181,-225,-87,-74,-227,-228,181,-340,181,181,181,-226,-229,181,181,-231,-230,]),'ELSE':([15,91,208,225,226,227,228,229,230,232,262,267,355,361,370,371,374,375,377,469,471,479,480,483,484,499,524,552,553,554,555,556,579,580,585,586,],[-71,-72,-339,-78,-79,-80,-81,-82,-83,-85,-73,-69,-221,-237,-87,-84,-233,-234,-236,-222,-224,-86,-84,-232,-235,-68,-223,-225,570,-84,-227,-228,-226,-229,-231,-230,]),'PPPRAGMASTR':([15,],[91,]),'EQUALS':([19,28,73,86,87,88,89,97,111,130,132,139,140,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,202,208,233,250,252,263,291,292,293,295,296,297,300,301,302,303,311,312,395,396,403,404,406,429,431,432,433,434,439,440,490,492,493,494,497,501,502,505,506,508,509,534,535,536,561,563,574,],[-52,-29,-181,135,-182,-54,-37,-53,195,-181,-55,-328,-30,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,332,-339,-315,379,-38,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-35,-36,489,-202,-43,-44,-308,-295,-296,-297,-298,-299,-31,-34,-203,-205,-39,-42,-278,-293,-294,-284,-285,-32,-33,-204,-40,-41,-302,-309,-303,]),'COMMA':([19,24,25,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,59,60,73,74,75,76,77,78,81,84,85,86,87,88,89,97,104,106,108,110,111,113,114,115,116,118,119,122,123,130,132,139,140,142,143,144,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,187,189,190,191,192,196,197,200,201,202,206,208,212,214,216,233,239,248,249,250,252,253,254,255,263,265,291,292,293,295,296,297,300,301,302,303,311,312,315,316,317,318,319,320,321,324,325,326,327,328,329,330,331,334,336,337,340,341,342,344,345,346,348,349,350,352,353,354,376,391,403,404,406,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,438,439,440,444,445,446,447,459,460,461,462,463,464,468,472,473,475,476,477,485,486,488,493,494,497,501,502,505,506,508,509,515,516,518,522,523,531,535,536,537,538,539,546,547,548,549,550,551,557,560,561,563,566,567,574,576,577,578,],[-52,-128,-102,-29,-107,-340,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-181,-98,-99,-100,-101,-104,-134,134,-135,-137,-182,-54,-37,-53,-103,-129,194,-139,-141,-183,-27,-28,-185,-169,-170,-149,-150,-181,-55,-328,-30,266,-306,-255,-256,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,313,314,-189,-194,-340,-184,-186,331,-174,-179,-152,-339,-145,-147,-340,-315,365,-238,-242,-277,-38,-136,-138,-196,-329,365,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-35,-36,-191,-192,-193,-207,-56,-1,-2,-45,-209,-140,-142,331,331,-171,-175,-154,-156,-151,-143,-144,-148,466,-164,-166,-146,-130,-206,-207,-177,-178,365,487,-43,-44,-308,365,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,365,503,-295,-313,-296,-297,-298,-299,507,-31,-34,-190,-195,-57,-208,-172,-173,-176,-180,-153,-155,-168,365,-240,-239,365,365,-243,-197,-199,-39,-42,-278,-293,-294,-284,-285,-32,-33,-210,-216,-214,-165,-167,-198,-40,-41,562,-257,-314,-50,-51,-212,-211,-213,-215,365,-200,-302,-309,-46,-49,-303,365,-47,-48,]),'RPAREN':([19,24,25,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,59,60,74,75,76,77,78,81,88,89,93,96,97,104,106,113,114,115,116,118,119,122,123,132,133,137,138,139,140,142,143,144,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,185,186,187,188,189,190,191,192,196,197,206,208,212,214,215,216,217,218,239,248,249,250,252,260,261,263,264,265,288,291,292,293,295,296,297,300,301,302,303,311,312,315,316,317,318,319,320,321,322,324,325,330,334,336,337,340,341,342,348,349,350,351,352,353,354,355,357,363,364,403,404,406,407,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,428,429,430,431,432,433,434,435,436,437,439,440,443,444,445,446,447,449,450,451,452,453,454,458,459,460,463,464,472,473,475,476,477,485,493,494,497,501,502,505,506,508,509,513,514,515,516,518,521,535,536,538,539,540,541,546,547,548,549,550,551,557,559,561,563,566,567,572,573,574,575,577,578,581,583,],[-52,-128,-102,-29,-107,-340,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-98,-99,-100,-101,-104,-134,-54,-37,140,-340,-53,-103,-129,-183,-27,-28,-185,-169,-170,-149,-150,-55,252,-340,262,-328,-30,267,-306,-255,-256,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,311,312,-187,-17,-18,-189,-194,-340,-184,-186,-152,-339,-145,-147,349,-340,353,354,-14,-238,-242,-277,-38,403,404,-329,405,406,429,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-35,-36,-191,-192,-193,-207,-56,-1,-2,-340,-45,-209,-171,-154,-156,-151,-143,-144,-148,-146,-130,-206,-340,-207,-177,-178,-221,-13,473,474,-43,-44,-308,499,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,502,-295,-313,-296,-297,-298,-299,504,505,506,-31,-34,-188,-190,-195,-57,-208,-340,515,516,-207,-23,-24,-340,-172,-173,-153,-155,525,-240,-239,526,527,-243,-39,-42,-278,-293,-294,-284,-285,-32,-33,546,547,-210,-216,-214,551,-40,-41,-257,-314,563,-310,-50,-51,-212,-211,-213,-215,571,-340,-302,-309,-46,-49,-340,582,-303,-311,-47,-48,584,-312,]),'COLON':([19,24,28,31,32,33,35,38,39,40,41,42,43,44,45,46,47,48,49,51,52,53,81,87,88,89,97,106,118,119,122,123,130,132,139,140,143,144,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,206,208,209,212,214,233,235,248,249,250,252,263,291,292,293,295,296,297,300,301,302,303,311,312,330,334,336,337,340,341,342,346,348,349,353,354,359,403,404,406,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,439,440,459,460,463,464,466,473,475,485,493,494,497,501,502,505,506,508,509,535,536,538,561,563,574,],[-52,-128,-29,-125,-126,-127,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-131,-132,-133,-134,-182,-54,-37,-53,-129,-169,-170,-149,-150,-181,-55,-328,-30,-306,-255,-256,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-152,-339,347,-145,-147,358,360,-238,-242,-277,-38,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-35,-36,-171,-154,-156,-151,-143,-144,-148,467,-146,-130,-177,-178,470,-43,-44,-308,500,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,-295,-296,-297,-298,-299,-31,-34,-172,-173,-153,-155,347,-240,-239,-243,-39,-42,-278,-293,-294,-284,-285,-32,-33,-40,-41,-257,-302,-309,-303,]),'LBRACKET':([19,24,25,28,29,30,31,32,33,34,35,38,39,40,41,42,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,59,60,74,75,76,77,78,81,88,89,97,104,106,113,114,115,116,118,119,121,122,123,132,139,140,143,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,192,196,197,206,208,212,214,216,233,252,256,263,291,292,300,301,302,303,311,312,318,319,322,324,325,330,334,336,337,340,341,342,348,349,351,352,353,354,395,396,403,404,406,429,431,432,433,434,439,440,446,447,452,459,460,463,464,487,490,492,493,494,498,501,502,508,509,515,516,518,534,535,536,540,541,546,547,548,549,550,551,561,562,563,566,567,574,575,577,578,583,],[95,-128,-102,-29,-107,-340,-125,-126,-127,-129,-241,-113,-114,-115,-116,-117,-118,-119,-120,-121,-122,-123,-124,-131,-132,-133,-105,-106,-108,-109,-110,-111,-112,-98,-99,-100,-101,-104,-134,136,-37,95,-103,-129,-183,-27,-28,-185,-169,-170,-338,-149,-150,136,-328,-30,-306,287,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,323,-184,-186,-152,-339,-145,-147,323,-315,-38,397,-329,-300,-301,-334,-335,-336,-337,-35,-36,323,448,323,-45,457,-171,-154,-156,-151,-143,-144,-148,-146,-130,323,323,-177,-178,397,-202,-43,-44,-308,-295,-296,-297,-298,-299,-31,-34,448,457,323,-172,-173,-153,-155,397,-203,-205,-39,-42,397,-293,-294,-32,-33,-210,-216,-214,-204,-40,-41,565,-310,-50,-51,-212,-211,-213,-215,-302,397,-309,-46,-49,-303,-311,-47,-48,-312,]),'RBRACKET':([51,52,53,81,95,114,115,116,136,139,143,144,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,184,197,208,248,249,250,257,259,263,291,292,293,295,296,297,300,301,302,303,305,306,307,308,323,399,400,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,429,431,432,433,434,441,442,448,455,456,457,473,475,485,491,495,496,497,501,502,505,506,510,512,517,519,520,538,542,543,561,563,568,569,574,576,],[-131,-132,-133,-134,-340,-27,-28,-185,-340,-328,-306,-255,-256,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-340,-28,-186,-339,-238,-242,-277,-340,-28,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,439,440,-3,-4,-340,493,494,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,501,-295,-296,-297,-298,-299,508,509,-340,-340,518,-340,-240,-239,-243,534,535,536,-278,-293,-294,-284,-285,-340,-28,548,549,550,-257,566,567,-302,-309,577,578,-303,583,]),'PERIOD':([121,139,143,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,256,263,291,292,300,301,302,303,395,396,406,429,431,432,433,434,487,490,492,498,501,502,534,540,541,561,562,563,574,575,583,],[-338,-328,-306,289,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,398,-329,-300,-301,-334,-335,-336,-337,398,-202,-308,-295,-296,-297,-298,-299,398,-203,-205,398,-293,-294,-204,564,-310,-302,398,-309,-303,-311,-312,]),'ARROW':([139,143,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,263,291,292,300,301,302,303,406,429,431,432,433,434,501,502,561,563,574,],[-328,-306,290,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-329,-300,-301,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-293,-294,-302,-309,-303,]),'CONDOP':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,268,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'DIVIDE':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,270,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,270,270,270,270,270,270,270,270,270,270,270,270,270,270,270,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'MOD':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,271,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,271,271,271,271,271,271,271,271,271,271,271,271,271,271,271,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'RSHIFT':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,274,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,274,274,274,274,274,274,274,274,274,274,274,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'LSHIFT':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,275,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,275,275,275,275,275,275,275,275,275,275,275,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'LT':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,276,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,276,276,276,276,276,276,276,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'LE':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,277,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,277,277,277,277,277,277,277,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'GE':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,278,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,278,278,278,278,278,278,278,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'GT':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,279,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,279,279,279,279,279,279,279,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'EQ':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,280,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,280,280,280,280,280,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'NE':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,281,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,281,281,281,281,281,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'OR':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,283,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,283,283,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'XOR':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,284,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,284,-274,284,284,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'LAND':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,285,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,285,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'LOR':([139,143,145,146,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,286,-258,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,-277,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-259,-260,-261,-262,-263,-264,-265,-266,-267,-268,-269,-270,-271,-272,-273,-274,-275,-276,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'XOREQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,380,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'TIMESEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,381,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'DIVEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,382,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'MODEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,383,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'PLUSEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,384,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'MINUSEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,385,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'LSHIFTEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,386,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'RSHIFTEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,387,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'ANDEQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,388,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'OREQUAL':([139,143,151,152,158,159,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,208,233,250,263,291,292,293,295,296,297,300,301,302,303,406,429,431,432,433,434,497,501,502,505,506,561,563,574,],[-328,-306,-277,-279,-292,-315,-304,-305,-307,-316,-317,-318,-319,-320,-321,-322,-323,-324,-325,-326,-327,-330,-331,-332,-333,-339,-315,389,-329,-300,-301,-280,-281,-282,-283,-334,-335,-336,-337,-308,-295,-296,-297,-298,-299,-278,-293,-294,-284,-285,-302,-309,-303,]),'ELLIPSIS':([313,],[443,]),}
+
+_lr_action = {}
+for _k, _v in _lr_action_items.items():
+   for _x,_y in zip(_v[0],_v[1]):
+      if not _x in _lr_action:  _lr_action[_x] = {}
+      _lr_action[_x][_k] = _y
+del _lr_action_items
+
+_lr_goto_items = {'translation_unit_or_empty':([0,],[1,]),'translation_unit':([0,],[2,]),'empty':([0,11,12,21,22,23,26,27,30,34,69,70,71,73,95,96,101,128,136,137,182,183,192,209,216,221,242,256,257,258,322,323,351,358,360,369,372,448,449,455,457,458,470,482,487,498,510,511,525,526,527,529,559,562,570,572,582,584,],[3,66,83,99,99,99,107,99,114,99,83,107,99,66,114,188,99,220,114,188,307,114,320,343,320,357,357,392,307,114,453,114,453,357,357,357,357,114,188,307,307,453,357,357,533,533,307,114,357,357,357,357,357,533,357,357,357,357,]),'external_declaration':([0,2,],[4,64,]),'function_definition':([0,2,],[5,5,]),'declaration':([0,2,11,67,73,128,221,372,],[6,6,68,129,68,223,223,482,]),'pp_directive':([0,2,],[7,7,]),'pppragma_directive':([0,2,124,128,203,204,205,221,242,333,335,358,360,369,470,525,526,527,570,582,584,],[8,8,211,231,211,211,211,231,371,211,211,371,371,480,371,554,371,371,371,371,371,]),'static_assert':([0,2,128,221,242,358,360,369,470,525,526,527,570,582,584,],[10,10,232,232,232,232,232,232,232,232,232,232,232,232,232,]),'id_declarator':([0,2,12,17,26,69,70,82,134,192,194,209,322,466,],[11,11,73,93,111,130,111,93,130,315,130,130,93,130,]),'declaration_specifiers':([0,2,11,67,73,96,128,137,221,313,322,351,372,449,458,],[12,12,69,69,69,192,69,192,69,192,192,192,69,192,192,]),'decl_body':([0,2,11,67,73,128,221,372,],[13,13,13,13,13,13,13,13,]),'direct_id_declarator':([0,2,12,17,20,26,69,70,80,82,134,192,194,209,318,322,452,466,],[19,19,19,19,97,19,19,19,97,19,19,19,19,19,97,19,97,19,]),'pointer':([0,2,12,17,26,69,70,82,113,134,192,194,209,216,322,351,466,],[20,20,80,20,20,80,20,80,196,80,318,80,80,352,452,352,80,]),'type_qualifier':([0,2,11,12,21,22,23,27,30,34,67,69,71,73,95,96,101,115,124,125,126,128,136,137,141,183,184,192,203,204,205,209,213,216,221,238,258,259,294,298,299,304,313,322,323,333,335,351,372,448,449,458,511,512,],[21,21,21,74,21,21,21,21,116,21,21,74,21,21,116,21,21,197,116,116,116,21,116,21,116,116,197,74,116,116,116,341,197,341,21,116,116,197,116,116,116,116,21,21,116,116,116,21,21,116,21,21,116,197,]),'storage_class_specifier':([0,2,11,12,21,22,23,27,34,67,69,71,73,96,101,128,137,192,221,313,322,351,372,449,458,],[22,22,22,75,22,22,22,22,22,22,75,22,22,22,22,22,22,75,22,22,22,22,22,22,22,]),'function_specifier':([0,2,11,12,21,22,23,27,34,67,69,71,73,96,101,128,137,192,221,313,322,351,372,449,458,],[23,23,23,76,23,23,23,23,23,23,76,23,23,23,23,23,23,76,23,23,23,23,23,23,23,]),'type_specifier_no_typeid':([0,2,11,12,26,67,69,70,73,96,124,125,126,128,137,141,192,193,203,204,205,209,213,216,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[24,24,24,77,24,24,77,24,24,24,24,24,24,24,24,24,77,24,24,24,24,340,24,340,24,24,24,24,24,24,24,24,24,24,24,24,24,24,]),'type_specifier':([0,2,11,26,67,70,73,96,124,125,126,128,137,141,193,203,204,205,213,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[25,25,25,104,25,104,25,25,212,212,212,25,25,212,104,212,212,212,348,25,212,212,212,212,212,25,25,212,212,25,25,25,25,]),'declaration_specifiers_no_type':([0,2,11,21,22,23,27,34,67,71,73,96,101,128,137,221,313,322,351,372,449,458,],[26,26,70,100,100,100,100,100,70,100,70,193,100,70,193,70,193,193,193,70,193,193,]),'alignment_specifier':([0,2,11,12,21,22,23,27,34,67,69,71,73,96,101,124,125,126,128,137,141,192,203,204,205,209,216,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[27,27,27,78,27,27,27,27,27,27,78,27,27,27,27,214,214,214,27,27,214,78,214,214,214,342,342,27,214,214,214,214,214,27,27,214,214,27,27,27,27,]),'typedef_name':([0,2,11,26,67,70,73,96,124,125,126,128,137,141,193,203,204,205,213,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,]),'enum_specifier':([0,2,11,26,67,70,73,96,124,125,126,128,137,141,193,203,204,205,213,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,]),'struct_or_union_specifier':([0,2,11,26,67,70,73,96,124,125,126,128,137,141,193,203,204,205,213,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,]),'atomic_specifier':([0,2,11,21,22,23,26,27,34,67,70,71,73,96,101,124,125,126,128,137,141,193,203,204,205,213,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[34,34,71,101,101,101,106,101,101,71,106,101,71,34,101,106,106,106,71,34,106,106,106,106,106,106,71,106,106,106,106,106,34,34,106,106,34,71,34,34,]),'struct_or_union':([0,2,11,26,67,70,73,96,124,125,126,128,137,141,193,203,204,205,213,221,238,294,298,299,304,313,322,333,335,351,372,449,458,],[37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,]),'declaration_list_opt':([11,73,],[65,131,]),'declaration_list':([11,73,],[67,67,]),'init_declarator_list_opt':([12,69,],[79,79,]),'init_declarator_list':([12,69,],[84,84,]),'init_declarator':([12,69,134,194,],[85,85,253,326,]),'declarator':([12,69,134,194,209,466,],[86,86,86,86,346,346,]),'typeid_declarator':([12,69,82,134,194,209,466,],[87,87,133,87,87,87,87,]),'direct_typeid_declarator':([12,69,80,82,134,194,209,466,],[88,88,132,88,88,88,88,88,]),'declaration_specifiers_no_type_opt':([21,22,23,27,34,71,101,],[98,102,103,112,117,117,117,]),'id_init_declarator_list_opt':([26,70,],[105,105,]),'id_init_declarator_list':([26,70,],[108,108,]),'id_init_declarator':([26,70,],[110,110,]),'type_qualifier_list_opt':([30,95,136,183,258,323,448,511,],[113,182,257,309,401,455,510,544,]),'type_qualifier_list':([30,95,124,125,126,136,141,183,203,204,205,238,258,294,298,299,304,323,333,335,448,511,],[115,184,213,213,213,259,213,115,213,213,213,213,115,213,213,213,213,115,213,213,512,115,]),'brace_open':([36,37,65,118,119,122,123,128,131,135,195,221,238,242,358,360,369,393,405,470,474,504,505,525,526,527,532,570,582,584,],[120,124,128,198,199,203,204,128,128,256,256,128,128,128,128,128,128,256,498,128,498,498,498,128,128,128,256,128,128,128,]),'compound_statement':([65,128,131,221,238,242,358,360,369,470,525,526,527,570,582,584,],[127,227,251,227,363,227,227,227,227,227,227,227,227,227,227,227,]),'unified_string_literal':([92,94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[138,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,407,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,]),'constant_expression':([94,126,234,332,347,397,467,],[142,218,359,462,468,491,523,]),'conditional_expression':([94,126,128,135,141,182,195,221,234,238,242,247,257,268,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,455,457,467,470,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[144,144,249,249,249,249,249,249,144,249,249,249,249,249,249,249,249,249,249,249,144,144,249,249,249,249,249,249,249,249,249,249,144,249,249,249,249,144,249,249,538,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,]),'binary_expression':([94,126,128,135,141,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,455,457,467,470,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[145,145,145,145,145,145,145,145,145,145,145,145,145,145,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,]),'cast_expression':([94,126,128,135,141,155,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[146,146,146,146,146,296,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,497,146,146,146,146,497,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,]),'unary_expression':([94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[151,151,250,250,250,293,295,151,297,250,250,250,151,250,250,250,250,250,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,250,250,250,250,250,250,151,151,250,250,250,250,250,250,250,250,250,250,151,250,250,151,250,250,151,250,151,250,151,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,]),'postfix_expression':([94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,]),'unary_operator':([94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,]),'primary_expression':([94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,]),'identifier':([94,96,126,128,135,137,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,314,332,347,358,360,362,365,366,367,369,372,378,393,397,398,401,402,405,449,455,457,467,470,474,482,500,503,507,510,525,526,527,528,529,532,544,545,559,564,565,570,572,582,584,],[162,191,162,162,162,191,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,445,162,162,162,162,162,162,162,162,162,162,162,162,162,492,162,162,162,191,162,162,162,162,162,162,162,162,541,162,162,162,162,162,162,162,162,162,162,575,162,162,162,162,162,]),'constant':([94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,]),'unified_wstring_literal':([94,126,128,135,141,153,154,155,156,182,195,221,234,238,242,247,257,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,294,298,309,310,332,347,358,360,362,365,366,367,369,372,378,393,397,401,402,405,455,457,467,470,474,482,500,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,]),'parameter_type_list':([96,137,322,351,449,458,],[185,260,454,454,513,454,]),'identifier_list_opt':([96,137,449,],[186,261,514,]),'parameter_list':([96,137,322,351,449,458,],[187,187,187,187,187,187,]),'identifier_list':([96,137,449,],[189,189,189,]),'parameter_declaration':([96,137,313,322,351,449,458,],[190,190,444,190,190,190,190,]),'enumerator_list':([120,198,199,],[200,328,329,]),'enumerator':([120,198,199,331,],[201,201,201,461,]),'struct_declaration_list':([124,203,204,],[205,333,335,]),'brace_close':([124,200,203,204,205,219,328,329,333,335,390,487,537,562,],[206,330,334,336,337,355,459,460,463,464,486,531,561,574,]),'struct_declaration':([124,203,204,205,333,335,],[207,207,207,338,338,338,]),'specifier_qualifier_list':([124,125,126,141,203,204,205,238,294,298,299,304,333,335,],[209,216,216,216,209,209,209,216,216,216,216,216,209,209,]),'type_name':([125,126,141,238,294,298,299,304,],[215,217,264,364,435,436,437,438,]),'block_item_list_opt':([128,],[219,]),'block_item_list':([128,],[221,]),'block_item':([128,221,],[222,356,]),'statement':([128,221,242,358,360,369,470,525,526,527,570,582,584,],[224,224,370,370,370,479,370,553,370,370,370,370,370,]),'labeled_statement':([128,221,242,358,360,369,470,525,526,527,570,582,584,],[225,225,225,225,225,225,225,225,225,225,225,225,225,]),'expression_statement':([128,221,242,358,360,369,470,525,526,527,570,582,584,],[226,226,226,226,226,226,226,226,226,226,226,226,226,]),'selection_statement':([128,221,242,358,360,369,470,525,526,527,570,582,584,],[228,228,228,228,228,228,228,228,228,228,228,228,228,]),'iteration_statement':([128,221,242,358,360,369,470,525,526,527,570,582,584,],[229,229,229,229,229,229,229,229,229,229,229,229,229,]),'jump_statement':([128,221,242,358,360,369,470,525,526,527,570,582,584,],[230,230,230,230,230,230,230,230,230,230,230,230,230,]),'expression_opt':([128,221,242,358,360,369,372,470,482,525,526,527,529,559,570,572,582,584,],[236,236,236,236,236,236,481,236,530,236,236,236,558,573,236,581,236,236,]),'expression':([128,141,221,238,242,247,268,287,294,298,358,360,362,366,367,369,372,470,482,525,526,527,528,529,559,565,570,572,582,584,],[239,265,239,265,239,376,408,427,265,265,239,239,472,476,477,239,239,239,239,239,239,239,557,239,239,576,239,239,239,239,]),'assignment_expression':([128,135,141,182,195,221,238,242,247,257,268,287,288,294,298,309,310,358,360,362,365,366,367,369,372,378,393,401,402,455,457,470,482,503,510,525,526,527,528,529,532,544,545,559,565,570,572,582,584,],[248,255,248,308,255,248,248,248,248,308,248,248,430,248,248,441,442,248,248,248,475,248,248,248,248,485,255,495,496,308,308,248,248,539,308,248,248,248,248,248,255,568,569,248,248,248,248,248,248,]),'initializer':([135,195,393,532,],[254,327,488,560,]),'assignment_expression_opt':([182,257,455,457,510,],[305,399,517,519,542,]),'typeid_noparen_declarator':([192,],[316,]),'abstract_declarator_opt':([192,216,],[317,350,]),'direct_typeid_noparen_declarator':([192,318,],[319,446,]),'abstract_declarator':([192,216,322,351,],[321,321,450,450,]),'direct_abstract_declarator':([192,216,318,322,351,352,452,],[325,325,447,325,325,447,447,]),'struct_declarator_list_opt':([209,],[339,]),'struct_declarator_list':([209,],[344,]),'struct_declarator':([209,466,],[345,522,]),'pragmacomp_or_statement':([242,358,360,470,525,526,527,570,582,584,],[368,469,471,524,552,555,556,579,585,586,]),'pppragma_directive_list':([242,358,360,470,525,526,527,570,582,584,],[369,369,369,369,369,369,369,369,369,369,]),'assignment_operator':([250,],[378,]),'initializer_list_opt':([256,],[390,]),'initializer_list':([256,498,],[391,537,]),'designation_opt':([256,487,498,562,],[393,532,393,532,]),'designation':([256,487,498,562,],[394,394,394,394,]),'designator_list':([256,487,498,562,],[395,395,395,395,]),'designator':([256,395,487,498,562,],[396,490,396,396,396,]),'argument_expression_list':([288,],[428,]),'parameter_type_list_opt':([322,351,458,],[451,451,521,]),'offsetof_member_designator':([507,],[540,]),}
+
+_lr_goto = {}
+for _k, _v in _lr_goto_items.items():
+   for _x, _y in zip(_v[0], _v[1]):
+       if not _x in _lr_goto: _lr_goto[_x] = {}
+       _lr_goto[_x][_k] = _y
+del _lr_goto_items
+_lr_productions = [
+  ("S' -> translation_unit_or_empty","S'",1,None,None,None),
+  ('abstract_declarator_opt -> empty','abstract_declarator_opt',1,'p_abstract_declarator_opt','plyparser.py',43),
+  ('abstract_declarator_opt -> abstract_declarator','abstract_declarator_opt',1,'p_abstract_declarator_opt','plyparser.py',44),
+  ('assignment_expression_opt -> empty','assignment_expression_opt',1,'p_assignment_expression_opt','plyparser.py',43),
+  ('assignment_expression_opt -> assignment_expression','assignment_expression_opt',1,'p_assignment_expression_opt','plyparser.py',44),
+  ('block_item_list_opt -> empty','block_item_list_opt',1,'p_block_item_list_opt','plyparser.py',43),
+  ('block_item_list_opt -> block_item_list','block_item_list_opt',1,'p_block_item_list_opt','plyparser.py',44),
+  ('declaration_list_opt -> empty','declaration_list_opt',1,'p_declaration_list_opt','plyparser.py',43),
+  ('declaration_list_opt -> declaration_list','declaration_list_opt',1,'p_declaration_list_opt','plyparser.py',44),
+  ('declaration_specifiers_no_type_opt -> empty','declaration_specifiers_no_type_opt',1,'p_declaration_specifiers_no_type_opt','plyparser.py',43),
+  ('declaration_specifiers_no_type_opt -> declaration_specifiers_no_type','declaration_specifiers_no_type_opt',1,'p_declaration_specifiers_no_type_opt','plyparser.py',44),
+  ('designation_opt -> empty','designation_opt',1,'p_designation_opt','plyparser.py',43),
+  ('designation_opt -> designation','designation_opt',1,'p_designation_opt','plyparser.py',44),
+  ('expression_opt -> empty','expression_opt',1,'p_expression_opt','plyparser.py',43),
+  ('expression_opt -> expression','expression_opt',1,'p_expression_opt','plyparser.py',44),
+  ('id_init_declarator_list_opt -> empty','id_init_declarator_list_opt',1,'p_id_init_declarator_list_opt','plyparser.py',43),
+  ('id_init_declarator_list_opt -> id_init_declarator_list','id_init_declarator_list_opt',1,'p_id_init_declarator_list_opt','plyparser.py',44),
+  ('identifier_list_opt -> empty','identifier_list_opt',1,'p_identifier_list_opt','plyparser.py',43),
+  ('identifier_list_opt -> identifier_list','identifier_list_opt',1,'p_identifier_list_opt','plyparser.py',44),
+  ('init_declarator_list_opt -> empty','init_declarator_list_opt',1,'p_init_declarator_list_opt','plyparser.py',43),
+  ('init_declarator_list_opt -> init_declarator_list','init_declarator_list_opt',1,'p_init_declarator_list_opt','plyparser.py',44),
+  ('initializer_list_opt -> empty','initializer_list_opt',1,'p_initializer_list_opt','plyparser.py',43),
+  ('initializer_list_opt -> initializer_list','initializer_list_opt',1,'p_initializer_list_opt','plyparser.py',44),
+  ('parameter_type_list_opt -> empty','parameter_type_list_opt',1,'p_parameter_type_list_opt','plyparser.py',43),
+  ('parameter_type_list_opt -> parameter_type_list','parameter_type_list_opt',1,'p_parameter_type_list_opt','plyparser.py',44),
+  ('struct_declarator_list_opt -> empty','struct_declarator_list_opt',1,'p_struct_declarator_list_opt','plyparser.py',43),
+  ('struct_declarator_list_opt -> struct_declarator_list','struct_declarator_list_opt',1,'p_struct_declarator_list_opt','plyparser.py',44),
+  ('type_qualifier_list_opt -> empty','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','plyparser.py',43),
+  ('type_qualifier_list_opt -> type_qualifier_list','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','plyparser.py',44),
+  ('direct_id_declarator -> ID','direct_id_declarator',1,'p_direct_id_declarator_1','plyparser.py',126),
+  ('direct_id_declarator -> LPAREN id_declarator RPAREN','direct_id_declarator',3,'p_direct_id_declarator_2','plyparser.py',126),
+  ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_id_declarator',5,'p_direct_id_declarator_3','plyparser.py',126),
+  ('direct_id_declarator -> direct_id_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_id_declarator',6,'p_direct_id_declarator_4','plyparser.py',126),
+  ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_id_declarator',6,'p_direct_id_declarator_4','plyparser.py',127),
+  ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_id_declarator',5,'p_direct_id_declarator_5','plyparser.py',126),
+  ('direct_id_declarator -> direct_id_declarator LPAREN parameter_type_list RPAREN','direct_id_declarator',4,'p_direct_id_declarator_6','plyparser.py',126),
+  ('direct_id_declarator -> direct_id_declarator LPAREN identifier_list_opt RPAREN','direct_id_declarator',4,'p_direct_id_declarator_6','plyparser.py',127),
+  ('direct_typeid_declarator -> TYPEID','direct_typeid_declarator',1,'p_direct_typeid_declarator_1','plyparser.py',126),
+  ('direct_typeid_declarator -> LPAREN typeid_declarator RPAREN','direct_typeid_declarator',3,'p_direct_typeid_declarator_2','plyparser.py',126),
+  ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_typeid_declarator',5,'p_direct_typeid_declarator_3','plyparser.py',126),
+  ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_typeid_declarator',6,'p_direct_typeid_declarator_4','plyparser.py',126),
+  ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_typeid_declarator',6,'p_direct_typeid_declarator_4','plyparser.py',127),
+  ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_typeid_declarator',5,'p_direct_typeid_declarator_5','plyparser.py',126),
+  ('direct_typeid_declarator -> direct_typeid_declarator LPAREN parameter_type_list RPAREN','direct_typeid_declarator',4,'p_direct_typeid_declarator_6','plyparser.py',126),
+  ('direct_typeid_declarator -> direct_typeid_declarator LPAREN identifier_list_opt RPAREN','direct_typeid_declarator',4,'p_direct_typeid_declarator_6','plyparser.py',127),
+  ('direct_typeid_noparen_declarator -> TYPEID','direct_typeid_noparen_declarator',1,'p_direct_typeid_noparen_declarator_1','plyparser.py',126),
+  ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_typeid_noparen_declarator',5,'p_direct_typeid_noparen_declarator_3','plyparser.py',126),
+  ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_typeid_noparen_declarator',6,'p_direct_typeid_noparen_declarator_4','plyparser.py',126),
+  ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_typeid_noparen_declarator',6,'p_direct_typeid_noparen_declarator_4','plyparser.py',127),
+  ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_typeid_noparen_declarator',5,'p_direct_typeid_noparen_declarator_5','plyparser.py',126),
+  ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LPAREN parameter_type_list RPAREN','direct_typeid_noparen_declarator',4,'p_direct_typeid_noparen_declarator_6','plyparser.py',126),
+  ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LPAREN identifier_list_opt RPAREN','direct_typeid_noparen_declarator',4,'p_direct_typeid_noparen_declarator_6','plyparser.py',127),
+  ('id_declarator -> direct_id_declarator','id_declarator',1,'p_id_declarator_1','plyparser.py',126),
+  ('id_declarator -> pointer direct_id_declarator','id_declarator',2,'p_id_declarator_2','plyparser.py',126),
+  ('typeid_declarator -> direct_typeid_declarator','typeid_declarator',1,'p_typeid_declarator_1','plyparser.py',126),
+  ('typeid_declarator -> pointer direct_typeid_declarator','typeid_declarator',2,'p_typeid_declarator_2','plyparser.py',126),
+  ('typeid_noparen_declarator -> direct_typeid_noparen_declarator','typeid_noparen_declarator',1,'p_typeid_noparen_declarator_1','plyparser.py',126),
+  ('typeid_noparen_declarator -> pointer direct_typeid_noparen_declarator','typeid_noparen_declarator',2,'p_typeid_noparen_declarator_2','plyparser.py',126),
+  ('translation_unit_or_empty -> translation_unit','translation_unit_or_empty',1,'p_translation_unit_or_empty','c_parser.py',509),
+  ('translation_unit_or_empty -> empty','translation_unit_or_empty',1,'p_translation_unit_or_empty','c_parser.py',510),
+  ('translation_unit -> external_declaration','translation_unit',1,'p_translation_unit_1','c_parser.py',518),
+  ('translation_unit -> translation_unit external_declaration','translation_unit',2,'p_translation_unit_2','c_parser.py',524),
+  ('external_declaration -> function_definition','external_declaration',1,'p_external_declaration_1','c_parser.py',534),
+  ('external_declaration -> declaration','external_declaration',1,'p_external_declaration_2','c_parser.py',539),
+  ('external_declaration -> pp_directive','external_declaration',1,'p_external_declaration_3','c_parser.py',544),
+  ('external_declaration -> pppragma_directive','external_declaration',1,'p_external_declaration_3','c_parser.py',545),
+  ('external_declaration -> SEMI','external_declaration',1,'p_external_declaration_4','c_parser.py',550),
+  ('external_declaration -> static_assert','external_declaration',1,'p_external_declaration_5','c_parser.py',555),
+  ('static_assert -> _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN','static_assert',6,'p_static_assert_declaration','c_parser.py',560),
+  ('static_assert -> _STATIC_ASSERT LPAREN constant_expression RPAREN','static_assert',4,'p_static_assert_declaration','c_parser.py',561),
+  ('pp_directive -> PPHASH','pp_directive',1,'p_pp_directive','c_parser.py',569),
+  ('pppragma_directive -> PPPRAGMA','pppragma_directive',1,'p_pppragma_directive','c_parser.py',580),
+  ('pppragma_directive -> PPPRAGMA PPPRAGMASTR','pppragma_directive',2,'p_pppragma_directive','c_parser.py',581),
+  ('pppragma_directive -> _PRAGMA LPAREN unified_string_literal RPAREN','pppragma_directive',4,'p_pppragma_directive','c_parser.py',582),
+  ('pppragma_directive_list -> pppragma_directive','pppragma_directive_list',1,'p_pppragma_directive_list','c_parser.py',592),
+  ('pppragma_directive_list -> pppragma_directive_list pppragma_directive','pppragma_directive_list',2,'p_pppragma_directive_list','c_parser.py',593),
+  ('function_definition -> id_declarator declaration_list_opt compound_statement','function_definition',3,'p_function_definition_1','c_parser.py',600),
+  ('function_definition -> declaration_specifiers id_declarator declaration_list_opt compound_statement','function_definition',4,'p_function_definition_2','c_parser.py',618),
+  ('statement -> labeled_statement','statement',1,'p_statement','c_parser.py',633),
+  ('statement -> expression_statement','statement',1,'p_statement','c_parser.py',634),
+  ('statement -> compound_statement','statement',1,'p_statement','c_parser.py',635),
+  ('statement -> selection_statement','statement',1,'p_statement','c_parser.py',636),
+  ('statement -> iteration_statement','statement',1,'p_statement','c_parser.py',637),
+  ('statement -> jump_statement','statement',1,'p_statement','c_parser.py',638),
+  ('statement -> pppragma_directive','statement',1,'p_statement','c_parser.py',639),
+  ('statement -> static_assert','statement',1,'p_statement','c_parser.py',640),
+  ('pragmacomp_or_statement -> pppragma_directive_list statement','pragmacomp_or_statement',2,'p_pragmacomp_or_statement','c_parser.py',688),
+  ('pragmacomp_or_statement -> statement','pragmacomp_or_statement',1,'p_pragmacomp_or_statement','c_parser.py',689),
+  ('decl_body -> declaration_specifiers init_declarator_list_opt','decl_body',2,'p_decl_body','c_parser.py',708),
+  ('decl_body -> declaration_specifiers_no_type id_init_declarator_list_opt','decl_body',2,'p_decl_body','c_parser.py',709),
+  ('declaration -> decl_body SEMI','declaration',2,'p_declaration','c_parser.py',769),
+  ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','c_parser.py',778),
+  ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','c_parser.py',779),
+  ('declaration_specifiers_no_type -> type_qualifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_1','c_parser.py',789),
+  ('declaration_specifiers_no_type -> storage_class_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_2','c_parser.py',794),
+  ('declaration_specifiers_no_type -> function_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_3','c_parser.py',799),
+  ('declaration_specifiers_no_type -> atomic_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_4','c_parser.py',806),
+  ('declaration_specifiers_no_type -> alignment_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_5','c_parser.py',811),
+  ('declaration_specifiers -> declaration_specifiers type_qualifier','declaration_specifiers',2,'p_declaration_specifiers_1','c_parser.py',816),
+  ('declaration_specifiers -> declaration_specifiers storage_class_specifier','declaration_specifiers',2,'p_declaration_specifiers_2','c_parser.py',821),
+  ('declaration_specifiers -> declaration_specifiers function_specifier','declaration_specifiers',2,'p_declaration_specifiers_3','c_parser.py',826),
+  ('declaration_specifiers -> declaration_specifiers type_specifier_no_typeid','declaration_specifiers',2,'p_declaration_specifiers_4','c_parser.py',831),
+  ('declaration_specifiers -> type_specifier','declaration_specifiers',1,'p_declaration_specifiers_5','c_parser.py',836),
+  ('declaration_specifiers -> declaration_specifiers_no_type type_specifier','declaration_specifiers',2,'p_declaration_specifiers_6','c_parser.py',841),
+  ('declaration_specifiers -> declaration_specifiers alignment_specifier','declaration_specifiers',2,'p_declaration_specifiers_7','c_parser.py',846),
+  ('storage_class_specifier -> AUTO','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',851),
+  ('storage_class_specifier -> REGISTER','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',852),
+  ('storage_class_specifier -> STATIC','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',853),
+  ('storage_class_specifier -> EXTERN','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',854),
+  ('storage_class_specifier -> TYPEDEF','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',855),
+  ('storage_class_specifier -> _THREAD_LOCAL','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',856),
+  ('function_specifier -> INLINE','function_specifier',1,'p_function_specifier','c_parser.py',861),
+  ('function_specifier -> _NORETURN','function_specifier',1,'p_function_specifier','c_parser.py',862),
+  ('type_specifier_no_typeid -> VOID','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',867),
+  ('type_specifier_no_typeid -> _BOOL','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',868),
+  ('type_specifier_no_typeid -> CHAR','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',869),
+  ('type_specifier_no_typeid -> SHORT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',870),
+  ('type_specifier_no_typeid -> INT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',871),
+  ('type_specifier_no_typeid -> LONG','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',872),
+  ('type_specifier_no_typeid -> FLOAT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',873),
+  ('type_specifier_no_typeid -> DOUBLE','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',874),
+  ('type_specifier_no_typeid -> _COMPLEX','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',875),
+  ('type_specifier_no_typeid -> SIGNED','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',876),
+  ('type_specifier_no_typeid -> UNSIGNED','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',877),
+  ('type_specifier_no_typeid -> __INT128','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',878),
+  ('type_specifier -> typedef_name','type_specifier',1,'p_type_specifier','c_parser.py',883),
+  ('type_specifier -> enum_specifier','type_specifier',1,'p_type_specifier','c_parser.py',884),
+  ('type_specifier -> struct_or_union_specifier','type_specifier',1,'p_type_specifier','c_parser.py',885),
+  ('type_specifier -> type_specifier_no_typeid','type_specifier',1,'p_type_specifier','c_parser.py',886),
+  ('type_specifier -> atomic_specifier','type_specifier',1,'p_type_specifier','c_parser.py',887),
+  ('atomic_specifier -> _ATOMIC LPAREN type_name RPAREN','atomic_specifier',4,'p_atomic_specifier','c_parser.py',893),
+  ('type_qualifier -> CONST','type_qualifier',1,'p_type_qualifier','c_parser.py',900),
+  ('type_qualifier -> RESTRICT','type_qualifier',1,'p_type_qualifier','c_parser.py',901),
+  ('type_qualifier -> VOLATILE','type_qualifier',1,'p_type_qualifier','c_parser.py',902),
+  ('type_qualifier -> _ATOMIC','type_qualifier',1,'p_type_qualifier','c_parser.py',903),
+  ('init_declarator_list -> init_declarator','init_declarator_list',1,'p_init_declarator_list','c_parser.py',908),
+  ('init_declarator_list -> init_declarator_list COMMA init_declarator','init_declarator_list',3,'p_init_declarator_list','c_parser.py',909),
+  ('init_declarator -> declarator','init_declarator',1,'p_init_declarator','c_parser.py',917),
+  ('init_declarator -> declarator EQUALS initializer','init_declarator',3,'p_init_declarator','c_parser.py',918),
+  ('id_init_declarator_list -> id_init_declarator','id_init_declarator_list',1,'p_id_init_declarator_list','c_parser.py',923),
+  ('id_init_declarator_list -> id_init_declarator_list COMMA init_declarator','id_init_declarator_list',3,'p_id_init_declarator_list','c_parser.py',924),
+  ('id_init_declarator -> id_declarator','id_init_declarator',1,'p_id_init_declarator','c_parser.py',929),
+  ('id_init_declarator -> id_declarator EQUALS initializer','id_init_declarator',3,'p_id_init_declarator','c_parser.py',930),
+  ('specifier_qualifier_list -> specifier_qualifier_list type_specifier_no_typeid','specifier_qualifier_list',2,'p_specifier_qualifier_list_1','c_parser.py',937),
+  ('specifier_qualifier_list -> specifier_qualifier_list type_qualifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_2','c_parser.py',942),
+  ('specifier_qualifier_list -> type_specifier','specifier_qualifier_list',1,'p_specifier_qualifier_list_3','c_parser.py',947),
+  ('specifier_qualifier_list -> type_qualifier_list type_specifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_4','c_parser.py',952),
+  ('specifier_qualifier_list -> alignment_specifier','specifier_qualifier_list',1,'p_specifier_qualifier_list_5','c_parser.py',957),
+  ('specifier_qualifier_list -> specifier_qualifier_list alignment_specifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_6','c_parser.py',962),
+  ('struct_or_union_specifier -> struct_or_union ID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','c_parser.py',970),
+  ('struct_or_union_specifier -> struct_or_union TYPEID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','c_parser.py',971),
+  ('struct_or_union_specifier -> struct_or_union brace_open struct_declaration_list brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_2','c_parser.py',981),
+  ('struct_or_union_specifier -> struct_or_union brace_open brace_close','struct_or_union_specifier',3,'p_struct_or_union_specifier_2','c_parser.py',982),
+  ('struct_or_union_specifier -> struct_or_union ID brace_open struct_declaration_list brace_close','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','c_parser.py',999),
+  ('struct_or_union_specifier -> struct_or_union ID brace_open brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_3','c_parser.py',1000),
+  ('struct_or_union_specifier -> struct_or_union TYPEID brace_open struct_declaration_list brace_close','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','c_parser.py',1001),
+  ('struct_or_union_specifier -> struct_or_union TYPEID brace_open brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_3','c_parser.py',1002),
+  ('struct_or_union -> STRUCT','struct_or_union',1,'p_struct_or_union','c_parser.py',1018),
+  ('struct_or_union -> UNION','struct_or_union',1,'p_struct_or_union','c_parser.py',1019),
+  ('struct_declaration_list -> struct_declaration','struct_declaration_list',1,'p_struct_declaration_list','c_parser.py',1026),
+  ('struct_declaration_list -> struct_declaration_list struct_declaration','struct_declaration_list',2,'p_struct_declaration_list','c_parser.py',1027),
+  ('struct_declaration -> specifier_qualifier_list struct_declarator_list_opt SEMI','struct_declaration',3,'p_struct_declaration_1','c_parser.py',1035),
+  ('struct_declaration -> SEMI','struct_declaration',1,'p_struct_declaration_2','c_parser.py',1073),
+  ('struct_declaration -> pppragma_directive','struct_declaration',1,'p_struct_declaration_3','c_parser.py',1078),
+  ('struct_declarator_list -> struct_declarator','struct_declarator_list',1,'p_struct_declarator_list','c_parser.py',1083),
+  ('struct_declarator_list -> struct_declarator_list COMMA struct_declarator','struct_declarator_list',3,'p_struct_declarator_list','c_parser.py',1084),
+  ('struct_declarator -> declarator','struct_declarator',1,'p_struct_declarator_1','c_parser.py',1092),
+  ('struct_declarator -> declarator COLON constant_expression','struct_declarator',3,'p_struct_declarator_2','c_parser.py',1097),
+  ('struct_declarator -> COLON constant_expression','struct_declarator',2,'p_struct_declarator_2','c_parser.py',1098),
+  ('enum_specifier -> ENUM ID','enum_specifier',2,'p_enum_specifier_1','c_parser.py',1106),
+  ('enum_specifier -> ENUM TYPEID','enum_specifier',2,'p_enum_specifier_1','c_parser.py',1107),
+  ('enum_specifier -> ENUM brace_open enumerator_list brace_close','enum_specifier',4,'p_enum_specifier_2','c_parser.py',1112),
+  ('enum_specifier -> ENUM ID brace_open enumerator_list brace_close','enum_specifier',5,'p_enum_specifier_3','c_parser.py',1117),
+  ('enum_specifier -> ENUM TYPEID brace_open enumerator_list brace_close','enum_specifier',5,'p_enum_specifier_3','c_parser.py',1118),
+  ('enumerator_list -> enumerator','enumerator_list',1,'p_enumerator_list','c_parser.py',1123),
+  ('enumerator_list -> enumerator_list COMMA','enumerator_list',2,'p_enumerator_list','c_parser.py',1124),
+  ('enumerator_list -> enumerator_list COMMA enumerator','enumerator_list',3,'p_enumerator_list','c_parser.py',1125),
+  ('alignment_specifier -> _ALIGNAS LPAREN type_name RPAREN','alignment_specifier',4,'p_alignment_specifier','c_parser.py',1136),
+  ('alignment_specifier -> _ALIGNAS LPAREN constant_expression RPAREN','alignment_specifier',4,'p_alignment_specifier','c_parser.py',1137),
+  ('enumerator -> ID','enumerator',1,'p_enumerator','c_parser.py',1142),
+  ('enumerator -> ID EQUALS constant_expression','enumerator',3,'p_enumerator','c_parser.py',1143),
+  ('declarator -> id_declarator','declarator',1,'p_declarator','c_parser.py',1158),
+  ('declarator -> typeid_declarator','declarator',1,'p_declarator','c_parser.py',1159),
+  ('pointer -> TIMES type_qualifier_list_opt','pointer',2,'p_pointer','c_parser.py',1271),
+  ('pointer -> TIMES type_qualifier_list_opt pointer','pointer',3,'p_pointer','c_parser.py',1272),
+  ('type_qualifier_list -> type_qualifier','type_qualifier_list',1,'p_type_qualifier_list','c_parser.py',1301),
+  ('type_qualifier_list -> type_qualifier_list type_qualifier','type_qualifier_list',2,'p_type_qualifier_list','c_parser.py',1302),
+  ('parameter_type_list -> parameter_list','parameter_type_list',1,'p_parameter_type_list','c_parser.py',1307),
+  ('parameter_type_list -> parameter_list COMMA ELLIPSIS','parameter_type_list',3,'p_parameter_type_list','c_parser.py',1308),
+  ('parameter_list -> parameter_declaration','parameter_list',1,'p_parameter_list','c_parser.py',1316),
+  ('parameter_list -> parameter_list COMMA parameter_declaration','parameter_list',3,'p_parameter_list','c_parser.py',1317),
+  ('parameter_declaration -> declaration_specifiers id_declarator','parameter_declaration',2,'p_parameter_declaration_1','c_parser.py',1336),
+  ('parameter_declaration -> declaration_specifiers typeid_noparen_declarator','parameter_declaration',2,'p_parameter_declaration_1','c_parser.py',1337),
+  ('parameter_declaration -> declaration_specifiers abstract_declarator_opt','parameter_declaration',2,'p_parameter_declaration_2','c_parser.py',1348),
+  ('identifier_list -> identifier','identifier_list',1,'p_identifier_list','c_parser.py',1380),
+  ('identifier_list -> identifier_list COMMA identifier','identifier_list',3,'p_identifier_list','c_parser.py',1381),
+  ('initializer -> assignment_expression','initializer',1,'p_initializer_1','c_parser.py',1390),
+  ('initializer -> brace_open initializer_list_opt brace_close','initializer',3,'p_initializer_2','c_parser.py',1395),
+  ('initializer -> brace_open initializer_list COMMA brace_close','initializer',4,'p_initializer_2','c_parser.py',1396),
+  ('initializer_list -> designation_opt initializer','initializer_list',2,'p_initializer_list','c_parser.py',1404),
+  ('initializer_list -> initializer_list COMMA designation_opt initializer','initializer_list',4,'p_initializer_list','c_parser.py',1405),
+  ('designation -> designator_list EQUALS','designation',2,'p_designation','c_parser.py',1416),
+  ('designator_list -> designator','designator_list',1,'p_designator_list','c_parser.py',1424),
+  ('designator_list -> designator_list designator','designator_list',2,'p_designator_list','c_parser.py',1425),
+  ('designator -> LBRACKET constant_expression RBRACKET','designator',3,'p_designator','c_parser.py',1430),
+  ('designator -> PERIOD identifier','designator',2,'p_designator','c_parser.py',1431),
+  ('type_name -> specifier_qualifier_list abstract_declarator_opt','type_name',2,'p_type_name','c_parser.py',1436),
+  ('abstract_declarator -> pointer','abstract_declarator',1,'p_abstract_declarator_1','c_parser.py',1448),
+  ('abstract_declarator -> pointer direct_abstract_declarator','abstract_declarator',2,'p_abstract_declarator_2','c_parser.py',1456),
+  ('abstract_declarator -> direct_abstract_declarator','abstract_declarator',1,'p_abstract_declarator_3','c_parser.py',1461),
+  ('direct_abstract_declarator -> LPAREN abstract_declarator RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_1','c_parser.py',1471),
+  ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_2','c_parser.py',1475),
+  ('direct_abstract_declarator -> LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_3','c_parser.py',1486),
+  ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET TIMES RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_4','c_parser.py',1496),
+  ('direct_abstract_declarator -> LBRACKET TIMES RBRACKET','direct_abstract_declarator',3,'p_direct_abstract_declarator_5','c_parser.py',1507),
+  ('direct_abstract_declarator -> direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',4,'p_direct_abstract_declarator_6','c_parser.py',1516),
+  ('direct_abstract_declarator -> LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_7','c_parser.py',1526),
+  ('block_item -> declaration','block_item',1,'p_block_item','c_parser.py',1537),
+  ('block_item -> statement','block_item',1,'p_block_item','c_parser.py',1538),
+  ('block_item_list -> block_item','block_item_list',1,'p_block_item_list','c_parser.py',1545),
+  ('block_item_list -> block_item_list block_item','block_item_list',2,'p_block_item_list','c_parser.py',1546),
+  ('compound_statement -> brace_open block_item_list_opt brace_close','compound_statement',3,'p_compound_statement_1','c_parser.py',1552),
+  ('labeled_statement -> ID COLON pragmacomp_or_statement','labeled_statement',3,'p_labeled_statement_1','c_parser.py',1558),
+  ('labeled_statement -> CASE constant_expression COLON pragmacomp_or_statement','labeled_statement',4,'p_labeled_statement_2','c_parser.py',1562),
+  ('labeled_statement -> DEFAULT COLON pragmacomp_or_statement','labeled_statement',3,'p_labeled_statement_3','c_parser.py',1566),
+  ('selection_statement -> IF LPAREN expression RPAREN pragmacomp_or_statement','selection_statement',5,'p_selection_statement_1','c_parser.py',1570),
+  ('selection_statement -> IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement','selection_statement',7,'p_selection_statement_2','c_parser.py',1574),
+  ('selection_statement -> SWITCH LPAREN expression RPAREN pragmacomp_or_statement','selection_statement',5,'p_selection_statement_3','c_parser.py',1578),
+  ('iteration_statement -> WHILE LPAREN expression RPAREN pragmacomp_or_statement','iteration_statement',5,'p_iteration_statement_1','c_parser.py',1583),
+  ('iteration_statement -> DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI','iteration_statement',7,'p_iteration_statement_2','c_parser.py',1587),
+  ('iteration_statement -> FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement','iteration_statement',9,'p_iteration_statement_3','c_parser.py',1591),
+  ('iteration_statement -> FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement','iteration_statement',8,'p_iteration_statement_4','c_parser.py',1595),
+  ('jump_statement -> GOTO ID SEMI','jump_statement',3,'p_jump_statement_1','c_parser.py',1600),
+  ('jump_statement -> BREAK SEMI','jump_statement',2,'p_jump_statement_2','c_parser.py',1604),
+  ('jump_statement -> CONTINUE SEMI','jump_statement',2,'p_jump_statement_3','c_parser.py',1608),
+  ('jump_statement -> RETURN expression SEMI','jump_statement',3,'p_jump_statement_4','c_parser.py',1612),
+  ('jump_statement -> RETURN SEMI','jump_statement',2,'p_jump_statement_4','c_parser.py',1613),
+  ('expression_statement -> expression_opt SEMI','expression_statement',2,'p_expression_statement','c_parser.py',1618),
+  ('expression -> assignment_expression','expression',1,'p_expression','c_parser.py',1625),
+  ('expression -> expression COMMA assignment_expression','expression',3,'p_expression','c_parser.py',1626),
+  ('assignment_expression -> LPAREN compound_statement RPAREN','assignment_expression',3,'p_parenthesized_compound_expression','c_parser.py',1638),
+  ('typedef_name -> TYPEID','typedef_name',1,'p_typedef_name','c_parser.py',1642),
+  ('assignment_expression -> conditional_expression','assignment_expression',1,'p_assignment_expression','c_parser.py',1646),
+  ('assignment_expression -> unary_expression assignment_operator assignment_expression','assignment_expression',3,'p_assignment_expression','c_parser.py',1647),
+  ('assignment_operator -> EQUALS','assignment_operator',1,'p_assignment_operator','c_parser.py',1660),
+  ('assignment_operator -> XOREQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1661),
+  ('assignment_operator -> TIMESEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1662),
+  ('assignment_operator -> DIVEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1663),
+  ('assignment_operator -> MODEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1664),
+  ('assignment_operator -> PLUSEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1665),
+  ('assignment_operator -> MINUSEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1666),
+  ('assignment_operator -> LSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1667),
+  ('assignment_operator -> RSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1668),
+  ('assignment_operator -> ANDEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1669),
+  ('assignment_operator -> OREQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1670),
+  ('constant_expression -> conditional_expression','constant_expression',1,'p_constant_expression','c_parser.py',1675),
+  ('conditional_expression -> binary_expression','conditional_expression',1,'p_conditional_expression','c_parser.py',1679),
+  ('conditional_expression -> binary_expression CONDOP expression COLON conditional_expression','conditional_expression',5,'p_conditional_expression','c_parser.py',1680),
+  ('binary_expression -> cast_expression','binary_expression',1,'p_binary_expression','c_parser.py',1688),
+  ('binary_expression -> binary_expression TIMES binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1689),
+  ('binary_expression -> binary_expression DIVIDE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1690),
+  ('binary_expression -> binary_expression MOD binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1691),
+  ('binary_expression -> binary_expression PLUS binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1692),
+  ('binary_expression -> binary_expression MINUS binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1693),
+  ('binary_expression -> binary_expression RSHIFT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1694),
+  ('binary_expression -> binary_expression LSHIFT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1695),
+  ('binary_expression -> binary_expression LT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1696),
+  ('binary_expression -> binary_expression LE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1697),
+  ('binary_expression -> binary_expression GE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1698),
+  ('binary_expression -> binary_expression GT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1699),
+  ('binary_expression -> binary_expression EQ binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1700),
+  ('binary_expression -> binary_expression NE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1701),
+  ('binary_expression -> binary_expression AND binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1702),
+  ('binary_expression -> binary_expression OR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1703),
+  ('binary_expression -> binary_expression XOR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1704),
+  ('binary_expression -> binary_expression LAND binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1705),
+  ('binary_expression -> binary_expression LOR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1706),
+  ('cast_expression -> unary_expression','cast_expression',1,'p_cast_expression_1','c_parser.py',1714),
+  ('cast_expression -> LPAREN type_name RPAREN cast_expression','cast_expression',4,'p_cast_expression_2','c_parser.py',1718),
+  ('unary_expression -> postfix_expression','unary_expression',1,'p_unary_expression_1','c_parser.py',1722),
+  ('unary_expression -> PLUSPLUS unary_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1726),
+  ('unary_expression -> MINUSMINUS unary_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1727),
+  ('unary_expression -> unary_operator cast_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1728),
+  ('unary_expression -> SIZEOF unary_expression','unary_expression',2,'p_unary_expression_3','c_parser.py',1733),
+  ('unary_expression -> SIZEOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','c_parser.py',1734),
+  ('unary_expression -> _ALIGNOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','c_parser.py',1735),
+  ('unary_operator -> AND','unary_operator',1,'p_unary_operator','c_parser.py',1743),
+  ('unary_operator -> TIMES','unary_operator',1,'p_unary_operator','c_parser.py',1744),
+  ('unary_operator -> PLUS','unary_operator',1,'p_unary_operator','c_parser.py',1745),
+  ('unary_operator -> MINUS','unary_operator',1,'p_unary_operator','c_parser.py',1746),
+  ('unary_operator -> NOT','unary_operator',1,'p_unary_operator','c_parser.py',1747),
+  ('unary_operator -> LNOT','unary_operator',1,'p_unary_operator','c_parser.py',1748),
+  ('postfix_expression -> primary_expression','postfix_expression',1,'p_postfix_expression_1','c_parser.py',1753),
+  ('postfix_expression -> postfix_expression LBRACKET expression RBRACKET','postfix_expression',4,'p_postfix_expression_2','c_parser.py',1757),
+  ('postfix_expression -> postfix_expression LPAREN argument_expression_list RPAREN','postfix_expression',4,'p_postfix_expression_3','c_parser.py',1761),
+  ('postfix_expression -> postfix_expression LPAREN RPAREN','postfix_expression',3,'p_postfix_expression_3','c_parser.py',1762),
+  ('postfix_expression -> postfix_expression PERIOD ID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1767),
+  ('postfix_expression -> postfix_expression PERIOD TYPEID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1768),
+  ('postfix_expression -> postfix_expression ARROW ID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1769),
+  ('postfix_expression -> postfix_expression ARROW TYPEID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1770),
+  ('postfix_expression -> postfix_expression PLUSPLUS','postfix_expression',2,'p_postfix_expression_5','c_parser.py',1776),
+  ('postfix_expression -> postfix_expression MINUSMINUS','postfix_expression',2,'p_postfix_expression_5','c_parser.py',1777),
+  ('postfix_expression -> LPAREN type_name RPAREN brace_open initializer_list brace_close','postfix_expression',6,'p_postfix_expression_6','c_parser.py',1782),
+  ('postfix_expression -> LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close','postfix_expression',7,'p_postfix_expression_6','c_parser.py',1783),
+  ('primary_expression -> identifier','primary_expression',1,'p_primary_expression_1','c_parser.py',1788),
+  ('primary_expression -> constant','primary_expression',1,'p_primary_expression_2','c_parser.py',1792),
+  ('primary_expression -> unified_string_literal','primary_expression',1,'p_primary_expression_3','c_parser.py',1796),
+  ('primary_expression -> unified_wstring_literal','primary_expression',1,'p_primary_expression_3','c_parser.py',1797),
+  ('primary_expression -> LPAREN expression RPAREN','primary_expression',3,'p_primary_expression_4','c_parser.py',1802),
+  ('primary_expression -> OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN','primary_expression',6,'p_primary_expression_5','c_parser.py',1806),
+  ('offsetof_member_designator -> identifier','offsetof_member_designator',1,'p_offsetof_member_designator','c_parser.py',1814),
+  ('offsetof_member_designator -> offsetof_member_designator PERIOD identifier','offsetof_member_designator',3,'p_offsetof_member_designator','c_parser.py',1815),
+  ('offsetof_member_designator -> offsetof_member_designator LBRACKET expression RBRACKET','offsetof_member_designator',4,'p_offsetof_member_designator','c_parser.py',1816),
+  ('argument_expression_list -> assignment_expression','argument_expression_list',1,'p_argument_expression_list','c_parser.py',1828),
+  ('argument_expression_list -> argument_expression_list COMMA assignment_expression','argument_expression_list',3,'p_argument_expression_list','c_parser.py',1829),
+  ('identifier -> ID','identifier',1,'p_identifier','c_parser.py',1838),
+  ('constant -> INT_CONST_DEC','constant',1,'p_constant_1','c_parser.py',1842),
+  ('constant -> INT_CONST_OCT','constant',1,'p_constant_1','c_parser.py',1843),
+  ('constant -> INT_CONST_HEX','constant',1,'p_constant_1','c_parser.py',1844),
+  ('constant -> INT_CONST_BIN','constant',1,'p_constant_1','c_parser.py',1845),
+  ('constant -> INT_CONST_CHAR','constant',1,'p_constant_1','c_parser.py',1846),
+  ('constant -> FLOAT_CONST','constant',1,'p_constant_2','c_parser.py',1865),
+  ('constant -> HEX_FLOAT_CONST','constant',1,'p_constant_2','c_parser.py',1866),
+  ('constant -> CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1882),
+  ('constant -> WCHAR_CONST','constant',1,'p_constant_3','c_parser.py',1883),
+  ('constant -> U8CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1884),
+  ('constant -> U16CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1885),
+  ('constant -> U32CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1886),
+  ('unified_string_literal -> STRING_LITERAL','unified_string_literal',1,'p_unified_string_literal','c_parser.py',1897),
+  ('unified_string_literal -> unified_string_literal STRING_LITERAL','unified_string_literal',2,'p_unified_string_literal','c_parser.py',1898),
+  ('unified_wstring_literal -> WSTRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1908),
+  ('unified_wstring_literal -> U8STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1909),
+  ('unified_wstring_literal -> U16STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1910),
+  ('unified_wstring_literal -> U32STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1911),
+  ('unified_wstring_literal -> unified_wstring_literal WSTRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1912),
+  ('unified_wstring_literal -> unified_wstring_literal U8STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1913),
+  ('unified_wstring_literal -> unified_wstring_literal U16STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1914),
+  ('unified_wstring_literal -> unified_wstring_literal U32STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1915),
+  ('brace_open -> LBRACE','brace_open',1,'p_brace_open','c_parser.py',1925),
+  ('brace_close -> RBRACE','brace_close',1,'p_brace_close','c_parser.py',1931),
+  ('empty -> <empty>','empty',0,'p_empty','c_parser.py',1937),
+]
diff --git a/app/__init__.py b/app/__init__.py
index 0b8a1bd33c78c189ece9f43eb20f5cb3f737934a..4b2f4ddc54adcc8634a48e7302e6197a76d62868 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -15,7 +15,7 @@ def create_app(config_class=Config):
     #ONLY TURN ON FOR DEBUGGING
     app.config['DEBUG'] = True
 
-    # Database connection , TO DO: Encrypt and move into .env
+    # Database connection , TO DO: Encrypt and move into .env fix where connection only works when remoted into database
     db_user = 'ethan2clay'
     db_password = 'Ethan2claY10+$++'
     db_name = 'ethan2clay_prj'
diff --git a/app/__pycache__/__init__.cpython-38.pyc b/app/__pycache__/__init__.cpython-38.pyc
index f9b5e6aefc8f097cd82ce91a1ebf644a85d16f11..c031af05a8b4e72cdf27dca3eebcd13dfa48f8e5 100644
Binary files a/app/__pycache__/__init__.cpython-38.pyc and b/app/__pycache__/__init__.cpython-38.pyc differ
diff --git a/app/api/routes.py b/app/api/routes.py
index 37e476e43be46cf1ef2fb2c0b753ec03dd7519da..f5665c4c2a6a28043f0941177afedc2188c5c764 100644
--- a/app/api/routes.py
+++ b/app/api/routes.py
@@ -12,7 +12,7 @@ def get_user_by_id(id):
             return jsonify({'error': 'User not found'}), 404
         
         user_data = {
-            'id': result.user_id,
+            'id': result.id,
             'username': result.username,
             'email': result.email
         }
@@ -25,13 +25,14 @@ def get_user_by_id(id):
 @bp.route('/user/create', methods=['GET'])
 def create_user():
     try:
+        #Hardcoded for now as when running upgrade on new db no users exist yet, will change at some point
         result = User.create_user('ethan_root', 'ethan2.clay@live.uwe.ac.uk', 'password1234', 1)
 
         if result is None:
             return jsonify({'error': 'User not found'}), 404
         
         user_data = {
-            'id': result.user_id,
+            'id': result.id,
             'username': result.username,
             'email': result.email
         }
diff --git a/app/bookings/routes.py b/app/bookings/routes.py
index 9a41ba3f7e40dccbdeab966e4a8eeecc50998997..70ade7f94e52700d66e1023bf5ed67087776871d 100644
--- a/app/bookings/routes.py
+++ b/app/bookings/routes.py
@@ -1,5 +1,6 @@
 from flask import render_template, redirect, url_for
 from app.bookings import bp
+from app.models import Listings 
 
 @bp.route('/home')
 def index():
@@ -8,3 +9,8 @@ def index():
 @bp.route('/')
 def redirect_index():
     return redirect(url_for('bookings.index'), code=301)
+
+@bp.route('/listings')
+def listings():
+    all_listings = Listings.get_all_listings()
+    return render_template('bookings/listings.html', all_listings=all_listings)
diff --git a/app/models/__init__.py b/app/models/__init__.py
index 58d05f855f4724a14b904d4dc2b9a8565958f971..e944908e51b64946bb66a68f07e8c91040f12bc8 100644
--- a/app/models/__init__.py
+++ b/app/models/__init__.py
@@ -1,2 +1,3 @@
 #Importing database models
-from .user import User
\ No newline at end of file
+from .user import User
+from .listings import Listings
\ No newline at end of file
diff --git a/app/models/listing_images.py b/app/models/listing_images.py
new file mode 100644
index 0000000000000000000000000000000000000000..7929183f0fcc3193a451226f201815d5caecd02c
--- /dev/null
+++ b/app/models/listing_images.py
@@ -0,0 +1,14 @@
+from flask import request, jsonify
+from app import db
+
+class ListingImages(db.Model):
+    __tablename__ = 'listing_images'
+
+    id = db.Column(db.Integer(), nullable=False, primary_key=True),
+    listing_id = db.Column(db.Integer(), nullable=False),
+    image_location = db.Column(db.String(255), nullable=False),
+    image_description = db.Column(db.String(255), nullable=True)
+
+    @classmethod
+    def get_all_listings(cls):
+        pass    
\ No newline at end of file
diff --git a/app/models/listings.py b/app/models/listings.py
new file mode 100644
index 0000000000000000000000000000000000000000..c210adac9786cee9dbbda55effc25864a9ed0882
--- /dev/null
+++ b/app/models/listings.py
@@ -0,0 +1,19 @@
+from flask import request, jsonify
+from app import db
+
+class Listings(db.Model):
+    __tablename__ = 'listings'
+
+    id = db.Column(db.Integer(), nullable=False, primary_key=True)
+    depart_location = db.Column(db.String(255), nullable=False)
+    depart_time = db.Column(db.DateTime(), nullable=False)
+    destination_location = db.Column(db.String(255), nullable=False)
+    destination_time = db.Column(db.DateTime(), nullable=False)
+    fair_cost = db.Column(db.Float(2), nullable=False)
+    transport_type = db.Column(db.String(255), nullable=False)
+    business_tickets = db.Column(db.Integer(), nullable=False)
+    economy_tickets = db.Column(db.Integer(), nullable=False)
+
+    @classmethod
+    def get_all_listings(cls):
+        return cls.query.all()    
\ No newline at end of file
diff --git a/app/models/user.py b/app/models/user.py
index e319fdf20f7d5dc5f86cb5729c2b8430cb980e74..c22b429a3b21f02f46691c3fbe05ad3543fc72b0 100644
--- a/app/models/user.py
+++ b/app/models/user.py
@@ -1,11 +1,10 @@
 from flask import request, jsonify
-from app.api import bp
 from app import db
 
 class User(db.Model):
     __tablename__ = 'users'
 
-    user_id = db.Column(db.Integer, primary_key=True)
+    id = db.Column(db.Integer, primary_key=True)
     username = db.Column(db.String(255), nullable=False, unique=True)
     email = db.Column(db.String(255), nullable=False, unique=True)
     password = db.Column(db.String(255), nullable=False)
diff --git a/app/static/base.css b/app/static/base.css
index 79416ff6df651d9e720777344c933a2fb9c825fe..29ac42ccc90e39f862dea0499b2f65273fb19724 100644
--- a/app/static/base.css
+++ b/app/static/base.css
@@ -110,6 +110,30 @@ body {
 }
 
 
+/* 
+    Button Css
+*/
+
+.button_1 {
+    background-color: #244855; 
+    border: none;
+    border-radius: 12px;
+    color: white;
+    padding: 15px 32px;
+    text-align: center;
+    text-decoration: none;
+    display: inline-block;
+    font-size: 16px;
+    cursor: pointer;
+}
+
+.button_1:hover {
+    background-color: #90AEAD;
+}
+
+
+
+
 /* HEX codes for colours used through out website don't delete */
 
 /* Colour pallete taken from https://visme.co/blog/website-color-schemes/
diff --git a/app/templates/bookings/index.html b/app/templates/bookings/index.html
index 547f91650053f49756483c7a3af2ffa536ec230e..e0e3f1e199047ff85af2a1982cb6f9bca6f6ab7e 100644
--- a/app/templates/bookings/index.html
+++ b/app/templates/bookings/index.html
@@ -2,6 +2,9 @@
 
 {% block content %}
     <div class="content">
-        <h2>Index of bookings.html</h2>
+        <h2>Top Deals For you</h2>
+            <h3>Create slide show of top bookings here<h3>
+
+            <div style="margin-left:50px"> <a href= "{{ url_for('bookings.listings') }}" class="button_1">View All Deals</a></div>
     </div>
 {% endblock %}
\ No newline at end of file
diff --git a/app/templates/bookings/listings.html b/app/templates/bookings/listings.html
new file mode 100644
index 0000000000000000000000000000000000000000..9cc31c53e35dea91f5b4b16d17c8e95cf134feff
--- /dev/null
+++ b/app/templates/bookings/listings.html
@@ -0,0 +1,8 @@
+{% extends 'base.html' %}
+
+{% block content %}
+    <div class="content">
+        <div style="margin-left:50px">Create Listings Logic</div>
+        <div style="margin-left:50px">{{all_listings}}</div>
+    </div>
+{% endblock %}
\ No newline at end of file
diff --git a/migrations/versions/489bab9aaf4f_add_listing_images_table.py b/migrations/versions/489bab9aaf4f_add_listing_images_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc5cb29abb12b34fcdfa29903271b8e2f7f59e8c
--- /dev/null
+++ b/migrations/versions/489bab9aaf4f_add_listing_images_table.py
@@ -0,0 +1,26 @@
+"""Add listing images table
+
+Revision ID: 489bab9aaf4f
+Revises: 6791cbf31235
+Create Date: 2024-11-05 11:13:50.215159
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '489bab9aaf4f'
+down_revision = '6791cbf31235'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+       op.create_table(
+              'listing_images',
+              sa.Column('id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True),
+              sa.Column('listing_id', sa.Integer(), nullable=False),
+              sa.Column('image_location', sa.String(255), nullable=False),
+              sa.Column('image_description', sa.String(255), nullable=True)
+       )
diff --git a/migrations/versions/6791cbf31235_add_listing_table.py b/migrations/versions/6791cbf31235_add_listing_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb5beb74f5b8be4595388be5d89f65568bc1d345
--- /dev/null
+++ b/migrations/versions/6791cbf31235_add_listing_table.py
@@ -0,0 +1,31 @@
+"""Add listing table
+
+Revision ID: 6791cbf31235
+Revises: ac9d4555724d
+Create Date: 2024-11-05 10:36:32.872815
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '6791cbf31235'
+down_revision = 'ac9d4555724d'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+       op.create_table(
+              'listings',
+              sa.Column('id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True),
+              sa.Column('depart_location', sa.String(255), nullable=False),
+              sa.Column('depart_time', sa.DateTime(), nullable=False),
+              sa.Column('destination_location', sa.String(255), nullable=False),
+              sa.Column('destination_time', sa.DateTime(), nullable=False),
+              sa.Column('fair_cost', sa.Float(2), nullable=False),
+              sa.Column('transport_type', sa.String(255), nullable=False),
+              sa.Column('business_tickets', sa.Integer(), nullable=False),
+              sa.Column('economy_tickets', sa.Integer(), nullable=False)
+       )
\ No newline at end of file
diff --git a/migrations/versions/ac9d4555724d_add_api_token_and_expiry.py b/migrations/versions/ac9d4555724d_add_api_token_and_expiry.py
index f6b781478c5c05b0dc26a2745c0383959b44f359..e93fdc531d938284d03cd0cd797f28dfed8b5ce9 100644
--- a/migrations/versions/ac9d4555724d_add_api_token_and_expiry.py
+++ b/migrations/versions/ac9d4555724d_add_api_token_and_expiry.py
@@ -19,7 +19,7 @@ depends_on = None
 def upgrade():
        op.create_table(
               'users',
-              sa.Column('user_id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True),
+              sa.Column('id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True),
               sa.Column('username', sa.String(255), nullable=False, unique=True),
               sa.Column('email', sa.String(255), nullable=False, unique=True),
               sa.Column('password', sa.String(255), nullable=False),