| # -*- coding: utf-8 -*- | 
 | # This file is part of Eigen, a lightweight C++ template library | 
 | # for linear algebra. | 
 | # | 
 | # Copyright (C) 2021 Huang, Zhaoquan <zhaoquan2008@hotmail.com> | 
 | # | 
 | # This Source Code Form is subject to the terms of the Mozilla Public | 
 | # License, v. 2.0. If a copy of the MPL was not distributed with this | 
 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
 |  | 
 | # Pretty printers for Eigen::Matrix to use with LLDB debugger | 
 | # | 
 | # Usage: | 
 | # 1. Add the following line (change it according to the path to this file) | 
 | #    to the file ~/.lldbinit (create one if it doesn't exist): | 
 | #        `command script import /path/to/eigenlldb.py` | 
 | # 2. Inspect the variables in LLDB command line | 
 | #        `frame variable` | 
 |  | 
 | import lldb | 
 | from typing import List | 
 | import bisect | 
 |  | 
 |  | 
 | def __lldb_init_module(debugger, internal_dict): | 
 |     debugger.HandleCommand("type synthetic add -x Eigen::Matrix<.*> --python-class eigenlldb.EigenMatrixChildProvider") | 
 |     debugger.HandleCommand( | 
 |         "type synthetic add -x Eigen::SparseMatrix<.*> --python-class eigenlldb.EigenSparseMatrixChildProvider") | 
 |  | 
 |  | 
 | class EigenMatrixChildProvider: | 
 |     _valobj: lldb.SBValue | 
 |     _scalar_type: lldb.SBType | 
 |     _scalar_size: int | 
 |     _rows_compile_time: int | 
 |     _cols_compile_time: int | 
 |     _row_major: bool | 
 |     _fixed_storage: bool | 
 |  | 
 |     def __init__(self, valobj, internal_dict): | 
 |         self._valobj = valobj | 
 |         valtype = valobj.GetType().GetCanonicalType() | 
 |  | 
 |         scalar_type = valtype.GetTemplateArgumentType(0) | 
 |         if not scalar_type.IsValid(): | 
 |             # In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion | 
 |             storage = valobj.GetChildMemberWithName("m_storage") | 
 |             data = storage.GetChildMemberWithName("m_data") | 
 |             data_type = data.GetType() | 
 |             if data_type.IsPointerType(): | 
 |                 scalar_type = data.GetType().GetPointeeType() | 
 |             else: | 
 |                 scalar_type = data.GetChildMemberWithName("array").GetType().GetArrayElementType() | 
 |         self._scalar_type = scalar_type | 
 |         self._scalar_size = self._scalar_type.GetByteSize() | 
 |  | 
 |         name = valtype.GetName() | 
 |         template_begin = name.find("<") | 
 |         template_end = name.find(">") | 
 |         template_args = name[(template_begin + 1):template_end].split(",") | 
 |         self._rows_compile_time = int(template_args[1]) | 
 |         self._cols_compile_time = int(template_args[2]) | 
 |         self._row_major = (int(template_args[3]) & 1) != 0 | 
 |  | 
 |         max_rows = int(template_args[4]) | 
 |         max_cols = int(template_args[5]) | 
 |         self._fixed_storage = (max_rows != -1 and max_cols != -1) | 
 |  | 
 |     def num_children(self): | 
 |         return self._cols() * self._rows() | 
 |  | 
 |     def get_child_index(self, name): | 
 |         pass | 
 |  | 
 |     def get_child_at_index(self, index): | 
 |         storage = self._valobj.GetChildMemberWithName("m_storage") | 
 |         data = storage.GetChildMemberWithName("m_data") | 
 |         offset = self._scalar_size * index | 
 |  | 
 |         if self._row_major: | 
 |             row = index // self._cols() | 
 |             col = index % self._cols() | 
 |         else: | 
 |             row = index % self._rows() | 
 |             col = index // self._rows() | 
 |         if self._fixed_storage: | 
 |             data = data.GetChildMemberWithName("array") | 
 |         if self._cols() == 1: | 
 |             name = '[{}]'.format(row) | 
 |         elif self._rows() == 1: | 
 |             name = '[{}]'.format(col) | 
 |         else: | 
 |             name = '[{},{}]'.format(row, col) | 
 |         return data.CreateChildAtOffset( | 
 |             name, offset, self._scalar_type | 
 |         ) | 
 |  | 
 |     def _cols(self): | 
 |         if self._cols_compile_time == -1: | 
 |             storage = self._valobj.GetChildMemberWithName("m_storage") | 
 |             cols = storage.GetChildMemberWithName("m_cols") | 
 |             return cols.GetValueAsUnsigned() | 
 |         else: | 
 |             return self._cols_compile_time | 
 |  | 
 |     def _rows(self): | 
 |         if self._rows_compile_time == -1: | 
 |             storage = self._valobj.GetChildMemberWithName("m_storage") | 
 |             rows = storage.GetChildMemberWithName("m_rows") | 
 |             return rows.GetValueAsUnsigned() | 
 |         else: | 
 |             return self._rows_compile_time | 
 |  | 
 |  | 
 | class EigenSparseMatrixChildProvider: | 
 |     _valobj: lldb.SBValue | 
 |     _scalar_type: lldb.SBType | 
 |     _scalar_size: int | 
 |     _index_type: lldb.SBType | 
 |     _index_size: int | 
 |     _row_major: bool | 
 |  | 
 |     _outer_size: int | 
 |     _nnz: int | 
 |     _values: lldb.SBValue | 
 |     _inner_indices: lldb.SBValue | 
 |     _outer_starts: lldb.SBValue | 
 |     _inner_nnzs: lldb.SBValue | 
 |     _compressed: bool | 
 |  | 
 |     # Index of the first synthetic child under each outer index | 
 |     _child_indices: List[int] | 
 |  | 
 |     def __init__(self, valobj, internal_dict): | 
 |         self._valobj = valobj | 
 |         valtype = valobj.GetType().GetCanonicalType() | 
 |         scalar_type = valtype.GetTemplateArgumentType(0) | 
 |         if not scalar_type.IsValid(): | 
 |             # In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion | 
 |             data = valobj.GetChildMemberWithName("m_data") | 
 |             values = data.GetChildMemberWithName("m_values") | 
 |             scalar_type = values.GetType().GetPointeeType() | 
 |         self._scalar_type = scalar_type | 
 |         self._scalar_size = scalar_type.GetByteSize() | 
 |  | 
 |         index_type = valtype.GetTemplateArgumentType(2) | 
 |         if not index_type.IsValid(): | 
 |             # In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion | 
 |             outer_starts = valobj.GetChildMemberWithName("m_outerIndex") | 
 |             index_type = outer_starts.GetType().GetPointeeType() | 
 |         self._index_type = index_type | 
 |         self._index_size = index_type.GetByteSize() | 
 |  | 
 |         name = valtype.GetName() | 
 |         template_begin = name.find("<") | 
 |         template_end = name.find(">") | 
 |         template_args = name[(template_begin + 1):template_end].split(",") | 
 |         self._row_major = (int(template_args[1]) & 1) != 0 | 
 |  | 
 |     def num_children(self): | 
 |         return self._nnz + 2 | 
 |  | 
 |     def get_child_index(self, name): | 
 |         pass | 
 |  | 
 |     def get_child_at_index(self, index): | 
 |         if index == 0: | 
 |             name = "rows" if self._row_major else "cols" | 
 |             return self._valobj.GetChildMemberWithName("m_outerSize") \ | 
 |                 .CreateChildAtOffset(name, 0, self._index_type) | 
 |         elif index == 1: | 
 |             name = "cols" if self._row_major else "rows" | 
 |             return self._valobj.GetChildMemberWithName("m_innerSize") \ | 
 |                 .CreateChildAtOffset(name, 0, self._index_type) | 
 |         else: | 
 |             index = index - 2 | 
 |         outer_index = bisect.bisect_right(self._child_indices, index) - 1 | 
 |         total_nnzs = self._child_indices[outer_index] | 
 |         if self._compressed: | 
 |             item_index = index | 
 |             inner_index = self._inner_indices \ | 
 |                 .CreateChildAtOffset("", item_index * self._index_size, self._index_type) \ | 
 |                 .GetValueAsUnsigned() | 
 |             return self._values \ | 
 |                 .CreateChildAtOffset(self._child_name(outer_index, inner_index), | 
 |                                      item_index * self._scalar_size, | 
 |                                      self._scalar_type) | 
 |         else: | 
 |             index_begin = self._outer_starts \ | 
 |                 .CreateChildAtOffset("", outer_index * self._index_size, self._index_type) \ | 
 |                 .GetValueAsUnsigned() | 
 |             item_index = index - total_nnzs + index_begin | 
 |             inner_index = self._inner_indices \ | 
 |                 .CreateChildAtOffset("", item_index * self._index_size, self._index_type) \ | 
 |                 .GetValueAsUnsigned() | 
 |             return self._values \ | 
 |                 .CreateChildAtOffset(self._child_name(outer_index, inner_index), | 
 |                                      item_index * self._scalar_size, | 
 |                                      self._scalar_type) | 
 |  | 
 |     def update(self): | 
 |         valobj = self._valobj | 
 |         self._outer_size = valobj.GetChildMemberWithName("m_outerSize").GetValueAsUnsigned() | 
 |         data = valobj.GetChildMemberWithName("m_data") | 
 |         self._values = data.GetChildMemberWithName("m_values") | 
 |         self._inner_indices = data.GetChildMemberWithName("m_indices") | 
 |         self._outer_starts = valobj.GetChildMemberWithName("m_outerIndex") | 
 |         self._inner_nnzs = valobj.GetChildMemberWithName("m_innerNonZeros") | 
 |  | 
 |         self._compressed = self._inner_nnzs.GetValueAsUnsigned() == 0 | 
 |  | 
 |         total_nnzs = 0 | 
 |         child_indices = [0] | 
 |         for outer_index in range(self._outer_size): | 
 |             if self._compressed: | 
 |                 index_end = self._outer_starts \ | 
 |                     .CreateChildAtOffset("", (outer_index + 1) * self._index_size, self._index_type) \ | 
 |                     .GetValueAsUnsigned() | 
 |                 total_nnzs = index_end | 
 |                 child_indices.append(total_nnzs) | 
 |             else: | 
 |                 nnzs = self._inner_nnzs \ | 
 |                     .CreateChildAtOffset("", outer_index * self._index_size, self._index_type) \ | 
 |                     .GetValueAsUnsigned() | 
 |                 total_nnzs = total_nnzs + nnzs | 
 |                 child_indices.append(total_nnzs) | 
 |         self._child_indices = child_indices | 
 |         self._nnz = total_nnzs | 
 |  | 
 |     def _child_name(self, outer_index, inner_index): | 
 |         if self._row_major: | 
 |             return "[{0},{1}]".format(outer_index, inner_index) | 
 |         else: | 
 |             return "[{1},{0}]".format(outer_index, inner_index) |