docs: def matrix(self, full=False, keeppads=True):
          """Return matrix form of the integral transform.
  
          Parameters
          ----------
          full : bool, optional
              when False return two vector factors and convolution matrix
              separately, otherwise return full transformation matrix
          keeppads : bool, optional
              whether to keep the padding in the output
  
          Returns
          -------
          If full is False, output separately
          a : (1, N) or (1, Nin) ndarray
              "After" factor, `_yfac_` or `yfac`
          b : (N,) or (Nin,) ndarray
              "Before" factor, `_xfac_` or `xfac`
          C : (N, N) or (Nin, Nin) ndarray
              Convolution matrix, circulant
  
          Otherwise, output the full matrix, combining `a`, `b`, and `C`
          M : (N, N) or (Nin, Nin) ndarray
              Full transformation matrix, `M = a * C * b`
  
          Notes
          -----
          `M`, `a`, `b`, and `C` are padded by default. Pads can be discarded if
          `keeppads` is set to False.
  
          This is not meant for evaluation with matrix multiplication but in case
          one is interested in the tranformation itself.
  
          When `N` is even and `lowring` is False, :math:`C C^{-1}` and :math:`M
          M^{-1}` can deviate from the identity matrix because the imaginary part
          of the Nyquist modes are dropped.
  
          The convolution matrix is a circulant matrix, with its first row and
          first column being the Fourier transform of :math:`u_m`.
          Indeed :math:`u_m` are the eigenvalues of the convolution matrix, that
          are diagonalized by the DFT matrix.
          Thus :math:`1/u_m` are the eigenvalues of the inverse convolution
          matrix.
          """
  
          v = np.fft.hfft(self._u, n=self.N) / self.N
          idx = sum(np.ogrid[0:self.N, -self.N:0])
          C = v[idx]  # follow scipy.linalg.{circulant,toeplitz,hankel}
  
          if keeppads:
              a = self._yfac_.copy()
              b = self._xfac_.copy()
          else:
              a = self.yfac.copy()
              b = self.xfac.copy()
              C = self._unpad(C, 0, True)
              C = self._unpad(C, 1, False)
          a = a.reshape(-1, 1)
  
          if not full:
              return a, b, C
          else:
              return a * C * b