text:
"""Lie algebra definitions relevant for supergravity."""

import itertools
import cached_property

from m_theory_lib import m_util as mu

import numpy
import scipy.linalg

# Naming deviates from PEP-8 conventions where this makes mathematics easier
# to read. Also, local variables may name-match module-global definitions,
# and we permit complex list comprehensions.
# pylint:disable=invalid-name
# pylint:disable=redefined-outer-name
# pylint: disable=g-complex-comprehension

nsum = mu.nsum  # Shorthand.

def get_normalizing_subalgebra(
    lie_gens_apq,
    subspace_ps,
    eigenvalue_threshold=1e-7):
  """Returns a [a, n]-basis for the subalgebra normalizing subspace_ps."""
  dim_large = lie_gens_apq.shape[1]
  dim_small = subspace_ps.shape[1]
  if ((not lie_gens_apq.shape[2] == dim_large == subspace_ps.shape[0]) or
      dim_small > dim_large):
    raise ValueError(
        'Bad shape of algebra generators / embedding: '
        f'lie_gens_apq.shape={lie_gens_apq.shape}, '
        f'subspace_ps.shape={subspace_ps.shape}')
  # The idea is very simple: If we start from vectors in subspace_ps.dot(ss),
  # and then project to some complement of the space spanned by the s vectors
  # of subspace_ps, this must be zero. Different complements differ by
  # adding to their basis vectors elements of subspace_ps,
  # but "as long as our bases are not too weird", such as when lengths of
  # and angles between basis vectors "are all reasonable", this is fine.
  svd_ps_u, svd_ps_s, svd_ps_vh = numpy.linalg.svd(subspace_ps,
                                                   full_matrices=True)
  del svd_ps_vh  # Unused, was only introduced for documentation completeness.
  onb_complement_s = (
      svd_ps_u[:, numpy.pad(svd_ps_s, [(0, dim_large - len(svd_ps_s))]) <=
               eigenvalue_threshold])
  act_project_rotated = nsum('apq,ps,qr->asr', lie_gens_apq, subspace_ps,
                             onb_complement_s)
  # We want to find those linear combinations of generators which,
  # when acting on vectors from the subspace, generate output
  # that, when projected to some complement of the subspace,
  # yields null. These are then generators which, when applied
  # to arbitrary subspace vectors, return other vectors whose
  # decomposition does not involve contributions going beyond that subspace.
  # This is the normalizer of the subspace.
  xu, xs, xvh = numpy.linalg.svd(
      act_project_rotated.reshape(act_project_rotated.shape[0], -1),
      full_matrices=True)
  del xvh  # Unused, named for documentation only.
  return xu[:, numpy.pad(xs, [(0, lie_gens_apq.shape[0] - len(xs))]) <=
            eigenvalue_threshold]

class Spin8:
  r"""Container class for Spin(8) tensor invariants.

  An instance essentially is just a namespace for constants.
  All attributes are to be considered as read-only by the user.

  Attributes:
    gamma_vsc:  The spin(8) gamma^i_{alpha,\dot\beta} gamma matrices, indexed by
      vector, spinor, co-spinor index.
    gamma_vvss: The spin(8) gamma^{ij}_{\alpha\beta}, indexed
      [i, j, \alpha, \beta].
    gamma_vvcc: The spin(8) gamma^{ij}_{\dot\alpha\dot\beta}, indexed
      [i, j, \alpha, \beta].
    gamma_sscc: The spin(8) gamma_{\alpha\beta\dot\delta\dot\epsilon}, indexed
      [\alpha, \beta, \dot\delta, \dot\epsilon].
    gamma_vvvvss: The spin(8) gamma^{ijkl}_{\alpha\beta}, indexed
      [i, j, k, l, \alpha, \beta].
    gamma_vvvvcc: The spin(8) gamma^{ijkl}_{\dot\alpha\dot\beta}, indexed
      [i, j, k, l, \dot\alpha, \dot\beta].
    gamma_sssscc: The spin(8)
      gamma^{\alpha\beta\gamma\delta}_{\dot\alpha\dot\beta}, indexed
      [\alpha, \beta, \gamma, \delta, \dot\alpha, \dot\beta].
    gamma_ccccvv: The spin(8)
      gamma^{\dot\alpha\dot\beta\dot\gamma\dot\delta}_{ij}, indexed
      [\dot\alpha, \dot\beta, \dot\gamma, \dot\delta, i, j].
    gamma_ssssvv: The spin(8)
      gamma^{\alpha\beta\gamma\delta}_{ij}, indexed
      [\alpha, \beta, \gamma, \delta, i, j].
    gamma_ccccss: The spin(8)
      gamma^{\dot\alpha\dot\beta\dot\gamma\dot\delta}_{\alpha\beta}, indexed
      [\dot\alpha, \dot\beta, \dot\gamma, \dot\delta, \alpha, \beta].
  """

  # Default conventions for lines-on-the-fano-plane.
  FANO_LINES = '124 156 137 235 267 346 457'
  # Their complements. The ordering of these is such that using them
  # to define SL(2)^7 inside E7 makes the holomorphic superpotential
  # of SO(8) supergravity match the 'Hamming code superpotential'
  # (7.5) in arXiv:1909.10969, with all coefficients +1, but with
  # the z-s relabeled such that we instead get the cubic terms
  # z_1 z_2 z_4 + z_1 z_5 z_6 + ..., in alignment with the above
  # FANO_LINES triplets that match the common 'octonion multiplication table'
  # definitions.
  FANO_QUARTETS = '1236 1257 1467 3567 2456 1345 2347'

  def __init__(self,
               conventions='gsw'
               ):
    """Initializes the instance.

    Args:
      conventions: str, the conventions to use for Gamma matrices. Options are:
        'gsw': Green, Schwarz, Witten, "Superstring Theory", Volume 1, (5.B.3).
        'octonionic': https://arxiv.org/abs/math/0105155, Table 1.
    """
    self._conventions = conventions
    self.gamma_vsc = gamma_vsc = self._get_gamma_vsc(conventions=conventions)
    #
    # The gamma^{ab}_{alpha beta} tensor that translates between antisymmetric
    # matrices over vectors [ij] and antisymmetric matrices over spinors [sS].
    # TODO(tfish): Change to mu.asymm2()
    self.gamma_vvss = 0.5 * (
        nsum('isc,jSc->ijsS', gamma_vsc, gamma_vsc) -
        nsum('jsc,iSc->ijsS', gamma_vsc, gamma_vsc))
    # The gamma^{ab}_{alpha* beta*} tensor that translates between antisymmetric
    # matrices over vectors [ij] and antisymmetric matrices over cospinors [cC].
    self.gamma_vvcc = 0.5 * (
        nsum('isc,jsC->ijcC', gamma_vsc, gamma_vsc) -
        nsum('jsc,isC->ijcC', gamma_vsc, gamma_vsc))
    # The gamma^{alpha beta}_{alpha* beta*} are not needed for the supergravity
    # computation per se, but we use these objects to determine the Spin(8)
    # rotation that brings E7 / SU(8)
    # scalar manifold coordinates into uniquely defined normal form.
    self.gamma_sscc = 0.5 * (
        nsum('vsc,vSC->sScC', gamma_vsc, gamma_vsc) -
        nsum('vsc,vSC->SscC', gamma_vsc, gamma_vsc))
    #
    # The gamma^{ijkl}_{alpha beta} tensor that translates between antisymmetric
    # 4-forms [ijkl] and symmetric traceless matrices over the spinors (sS),
    # as well as its co-spinor (cC) cousin.
    g_ijsS = nsum('isc,jSc->ijsS', self.gamma_vsc, self.gamma_vsc)
    g_ijcC = nsum('isc,jsC->ijcC', self.gamma_vsc, self.gamma_vsc)
    g_ijklsS = nsum('ijst,kltS->ijklsS', g_ijsS, g_ijsS)
    g_ijklcC = nsum('ijcd,kldC->ijklcC', g_ijcC, g_ijcC)
    gamma_vvvvss = numpy.zeros([8] * 6)
    gamma_vvvvcc = numpy.zeros([8] * 6)
    for perm in itertools.permutations(range(4)):
      perm_ijkl = ''.join('ijkl'[p] for p in perm)
      sign = mu.permutation_sign(perm)
      gamma_vvvvss += sign * nsum(perm_ijkl + 'sS->ijklsS', g_ijklsS)
      gamma_vvvvcc += sign * nsum(perm_ijkl + 'cC->ijklcC', g_ijklcC)
    self.gamma_vvvvss = gamma_vvvvss / 24.0
    self.gamma_vvvvcc = gamma_vvvvcc / 24.0
    # The other gamma_aaaabb are sometimes also useful, especially when
    # considering e7 subalgebras of sl(8) ~ so(8) + 35s.
    g_stuvcC = nsum('stcC,uvCd->stuvcd', self.gamma_sscc, self.gamma_sscc)
    gamma_sssscc = numpy.zeros([8] * 6)
    for perm in itertools.permutations(range(4)):
      perm_stuv = ''.join('stuv'[p] for p in perm)
      sign = mu.permutation_sign(perm)
      gamma_sssscc += sign * nsum(perm_stuv + 'cC->stuvcC', g_stuvcC)
    self.gamma_sssscc = gamma_sssscc / 24.0
    gamma_ccccvv = numpy.zeros([8] * 6)
    g_cdefvV = nsum('ijcd,jkef->cdefik', self.gamma_vvcc, self.gamma_vvcc)
    for perm in itertools.permutations(range(4)):
      perm_cdef = ''.join('cdef'[p] for p in perm)
      sign = mu.permutation_sign(perm)
      gamma_ccccvv += sign * nsum(perm_cdef + 'vV->cdefvV', g_cdefvV)
    self.gamma_ccccvv = gamma_ccccvv / 24.0
    #
    gamma_ssssvv = numpy.zeros([8] * 6)
    g_stuvij = nsum('ijst,jkuv->stuvik', self.gamma_vvss, self.gamma_vvss)
    for perm in itertools.permutations(range(4)):
      perm_stuv = ''.join('stuv' [p] for p in perm)
      sign = mu.permutation_sign(perm)
      gamma_ssssvv += sign * nsum(perm_stuv + 'ij->stuvij', g_stuvij)
    self.gamma_ssssvv = gamma_ssssvv / 24.0
    #
    gamma_ccccss = numpy.zeros([8] * 6)
    g_cdefsS = nsum('stcd,tuef->cdefsu', self.gamma_sscc, self.gamma_sscc)
    for perm in itertools.permutations(range(4)):
      perm_cdef = ''.join('cdef' [p] for p in perm)
      sign = mu.permutation_sign(perm)
      gamma_ccccss += sign * nsum(perm_cdef + 'sS->cdefsS', g_cdefsS)
    self.gamma_ccccss = gamma_ccccss / 24.0

  def _get_gamma_vsc(self, conventions='gsw'):
    r"""Computes Spin(8) gamma-matrices.

    Args:
      conventions: str, the conventions to use for Gamma matrices. Options are:
        'gsw': Green, Schwarz, Witten, "Superstring Theory", Volume 1, (5.B.3).
        'octonionic': https://arxiv.org/abs/math/0105155, Table 1.
        Adding a suffix '/svc', '/csv', '/vcs', etc. will apply a
        triality-rotation that re-labels the 8-dimensional representations, e.g.
        'gsw/vcs' will build Green-Schwarz-Witten Gamma matrices, but
        use 'vsc->vcs' re-labeling to swap the "spinor" and "co-spinor"
        representations (so, swapping the spinor and co-spinor gamma-eigenvalues
        (+1, -1) -> (-1, +1)).

    Returns:
      [8, 8, 8]-tensor gamma^i_\alpha\dot\beta.
    """
    ret = numpy.zeros([8, 8, 8])
    if conventions.startswith('gsw'):
      # Conventions for Spin(8) gamma matrices match Green, Schwarz, Witten,
      # but with indices shifted down by 1 to the range [0 .. 7].
      entries = (
          '007+ 016- 025- 034+ 043- 052+ 061+ 070- '
          '101+ 110- 123- 132+ 145+ 154- 167- 176+ '
          '204+ 215- 226+ 237- 240- 251+ 262- 273+ '
          '302+ 313+ 320- 331- 346- 357- 364+ 375+ '
          '403+ 412- 421+ 430- 447+ 456- 465+ 474- '
          '505+ 514+ 527+ 536+ 541- 550- 563- 572- '
          '606+ 617+ 624- 635- 642+ 653+ 660- 671- '
          '700+ 711+ 722+ 733+ 744+ 755+ 766+ 777+')
      for ijkc in entries.split():
        indices = tuple([int(m) for m in ijkc[:-1]])
        sign = 1 if ijkc[-1] == '+' else -1
        ret[indices] = sign
    elif conventions.startswith('octonionic'):
      for n in range(1, 8):
        ret[0, n, n] = -1
        ret[n, n, 0] = ret[n, 0, n] = 1
      ret[0, 0, 0] = 1
      for cijk in self.FANO_LINES.split():
        ijk = tuple(int(idx) for idx in cijk)
        for p, q, r in ((0, 1, 2), (1, 2, 0), (2, 0, 1)):
          # Note that we have to `go against the direction of the arrows'
          # to make the correspondence work.
          ret[ijk[r], ijk[p], ijk[q]] = -1
          ret[ijk[r], ijk[q], ijk[p]] = +1
    else:
      raise ValueError('Unknown spin(8) conventions: %r' % conventions)
    pos_slash = conventions.find('/')
    if pos_slash == -1:
      return ret
    return nsum('vsc->' + conventions[pos_slash + 1:], ret)

  def get_diagonalizing_rotation(self, m8x8):
    """Finds a SO(8) rotation that diagonalizes a symmetric 8x8 matrix."""
    eigvals, basis = scipy.linalg.eigh(m8x8)
    eigvecs = basis.T[[i for i, _ in sorted(enumerate(eigvals),
                                            key=lambda iv: -iv[1])]]
    det = numpy.linalg.det(eigvecs)
    assert abs(abs(det) - 1.0) < 1e-6, 'Eigenbasis not orthonormal'
    if det < 0:
      # Make eigenvector basis right-handed by reversing last eigenvector.
      eigvecs[-1] *= -1
    return eigvecs.T

class SU8:
  """Container class for su(8) tensor invariants.

  An instance essentially is just a namespace for constants.
  All attributes are to be considered as read-only by the user.

  Attributes:
    index56_and_coeff_by_ijk: dict mapping triplet (i, j, k) of three different
      su(8) indices to a pair of a 56-index and a sign factor (+1 or -1).
    ij_map: Lexicographically sorted list of pairs of su(8) indices
      (i, j) with i < j.
    inv_ij_map: Mapping that maps an (i, j)-key with i < j to the corresponding
      index of the pair in lexical ordering.
    m_35_8_8: [35, 8, 8]-array mapping a 35-index to a symmetric traceless
      matrix. Each such matrix has two entries of magnitude 1.
      The first 7 (8, 8) matrices are the lexicographically ordered matrices
      of the form diag(0, ..., 0, 1, -1, 0, ..., 0). The remaining 28 have
      a 1 in (i, j) and (j, i)-position and are zero otherwise.
    m_28_8_8: [28, 8, 8]-array mapping a 28-index to an antisymmetric traceless
      matrix. Each such matrix has one entry +1, one entry -1, and otherwise
      zeros. The locations of the +1s correspond to ij_map.
    ginv35: [35, 35]-array, inverse of the inner-products matrix for
      the 35-irrep of so(8) above that expands so(8) to su(8).
    m_56_8_8_8: [56, 8, 8, 8]-array mapping a 56-index to an antisymmetric
      [8, 8, 8]-array (or vice versa).
    m_action_56_56_8_8: [56, 56, 8, 8]-array mapping an 8x8 generator matrix
      to the corresponding action on the 56-irrep equivalent to [ijk].
    eps_56_56_8_8: epsilon^{ijklmnpq} with index groups (ijk) and (lmn) mapped
      to a 56-index.
    t_aij: su(8) generators (T_a)^j{}_i = t_aij[a, i, j].
  """

  def __init__(self):
    # Translates between adjoint indices 'a' and (vector) x (vector)
    # indices 'ij'.
    ij_map = [(i, j) for i in range(8) for j in range(8) if i < j]
    #
    # We also need the mapping between 8 x 8 and 35 representations of the
    # underlying spin(8), using common conventions for a basis of the
    # 35-representation, and likewise for 8 x 8 and 28.
    # These are used in various places, often with real-only quantities,
    # so we use dtype=float here, even though they also are used in complex
    # context.
    m_35_8_8 = numpy.zeros([35, 8, 8], dtype=numpy.float64)
    m_28_8_8 = numpy.zeros([28, 8, 8], dtype=numpy.float64)
    for n in range(7):
      m_35_8_8[n, n, n] = +1.0
      m_35_8_8[n, n + 1, n + 1] = -1.0
    for a, (m, n) in enumerate(ij_map):
      m_35_8_8[a + 7, m, n] = m_35_8_8[a + 7, n, m] = 1.0
      m_28_8_8[a, m, n] = 1.0
      m_28_8_8[a, n, m] = -1.0
    #
    # The su8 'Generator Matrices'.
    t_aij = numpy.zeros([63, 8, 8], dtype=numpy.complex128)
    t_aij[:35, :, :] = 1.0j * m_35_8_8
    print('IMPORTANT: Changed SO(8)-in-SU(8) definitions')
    # TODO(tfish): Apparently, with the definitions from the Big Paper,
    # the A1-tensor and hence gravitino masses come out wrong when
    # using the Theta-tensor formalism without this SO(8) sign change.
    # We will hence also need to fix so8_supergravity_extrema/:
    #  - Change distillation to diagonalize using an extra factor -1
    #    for so8 generators.
    #  - remove so8_supergravity_extrema/algebra.py - this is now unneeded.
    #  - add tests.
    for a, (i, j) in enumerate(ij_map):
      t_aij[a + 35, i, j] = -1.0  # Was: 1.0
      t_aij[a + 35, j, i] = +1.0  # Was: -1.0
    #
    # We also need to be able to map [ijk] to a linear 56-index.
    # Our choice of signs for the ijk-basis is essentially arbitrary here.
    # We lexicographically order triplets and attribute a + sign to
    # every first occurrence of a particular combination.
    index56_and_coeff_by_ijk = {}
    ijk_by_index56 = {}
    num_ijk = 0
    for i in range(8):
      for j in range(i + 1, 8):
        for k in range(j + 1, 8):
          ijk = (i, j, k)
          index56 = num_ijk
          ijk_by_index56[index56] = ijk
          num_ijk += 1
          for p in ((0, 1, 2), (1, 2, 0), (2, 0, 1)):
            for q_sign, q in ((1, (0, 1, 2)), (-1, (1, 0, 2))):
              pq_ijk = (ijk[p[q[0]]], ijk[p[q[1]]], ijk[p[q[2]]])
              index56_and_coeff_by_ijk[pq_ijk] = (index56, q_sign)
    # Let us also provide this as an (actually rather sparse) tensor.
    # We will only use this very occasionally.
    m_56_8_8_8 = numpy.zeros([56, 8, 8, 8])
    for ijk, (i56, sign) in index56_and_coeff_by_ijk.items():
      m_56_8_8_8[i56, ijk[0], ijk[1], ijk[2]] = sign
    # Supergravity has the "fermion mass" tensor
    # A3^ijk,lmn = (sqrt(2) / 144) * eps^ijkpqr[lm A2^n]_pqr
    # This structure suggests that it is numerically convenient
    # to have epsilon as a 56 x 56 x 8 x 8 tensor.
    eps_56_56_8_8 = numpy.zeros([56, 56, 8, 8])
    for p8 in itertools.permutations(range(8)):
      sign8 = mu.permutation_sign(p8)
      i56, coeff_i56 = index56_and_coeff_by_ijk[p8[:3]]
      j56, coeff_j56 = index56_and_coeff_by_ijk[p8[3: 6]]
      eps_56_56_8_8[i56, j56, p8[6], p8[7]] = sign8 * coeff_i56 * coeff_j56
    # Also, we need to know how su(8) elements given as 8x8 matrices act on this
    # 56x56-basis.
    m_action_56_56_8_8 = numpy.zeros([56, 56, 8, 8])
    for index56_left, ijk_left in ijk_by_index56.items():
      for index56_right, ijk_right in ijk_by_index56.items():
        common_indices = set(ijk_left) & set(ijk_right)
        if len(common_indices) != 2:
          continue
        # Two indices are the same, one gets transformed by the generator.
        transforming_index_left = [idx for idx in ijk_left
                                   if idx not in common_indices][0]
        transforming_index_right = [idx for idx in ijk_right
                                    if idx not in common_indices][0]
        transformed_ijk_left = [
            transforming_index_left if idx == transforming_index_right else idx
            for idx in ijk_right]
        sign = mu.permutation_sign([ijk_left.index(i)
                                    for i in transformed_ijk_left])
        m_action_56_56_8_8[
            index56_left, index56_right,
            transforming_index_left, transforming_index_right] = sign
    #
    self.index56_and_coeff_by_ijk = index56_and_coeff_by_ijk
    self.ij_map = ij_map
    self.inv_ij_map = {ij: num_ij for num_ij, ij in enumerate(ij_map)}
    self.m_35_8_8 = m_35_8_8
    self.ginv35 = numpy.linalg.inv(
        nsum('aij,bij->ab', m_35_8_8, m_35_8_8))
    self.m_28_8_8 = m_28_8_8
    self.m_56_8_8_8 = m_56_8_8_8
    self.eps_56_56_8_8 = eps_56_56_8_8
    self.m_action_56_56_8_8 = m_action_56_56_8_8
    self.t_aij = t_aij

class E7:
  r"""Container class for e7 tensor invariants.

  An instance essentially is just a namespace for constants.
  All attributes are to be considered as read-only by the user.

  Due to triality, we have freedom which 8-dimensional spin(8)
  representation to call the 'vector', 'spinor', and 'co-spinor'
  representation. For convenience, we here call the 8-dimensional
  representation whose symmetric product with itself provides
  compact directions in su(8) the 'vector' representation, and the other
  two representations the 'spinor' and 'co-spinor' representation, as this gives
  a mostly-symmetric role to spinors and co-spinors. These conventions deviate
  from some of the supergravity literature, but are convenient here.

  Attributes:
    t_a_ij_kl: [133, 56, 56]-array of e7(+7) generators
      (T_a)^{kl}{}_{ij} = t_aij[a, ij, kl] for the 56-dimensional fundamental
      irreducible representation. The 56-indices split into two pairs of
      28-indices that are antisymmetric index-pairs of the 8-dimensional su(8)
      representation. The first 70 of the 133 generators are the 35+35
      noncompact directions corresponding to the scalars of SO(8) supergravity.
      The subsequent 63 form the maximal compact subalgebra su(8).
    inv_gramian70: [70, 70]-array. Inverse inner product matrix of the first
      70 basis vectors. All entries in this matrix are exact (as integer
      multiples of 1/8) despite being float. This property can be relied on
      for high-accuracy computations.
    v70_as_sc8x8: [70, 2, 8, 8]-array that decomposes an e7 generator in
      e7/su(8) into two sets of symmetric traceless 8x8 matrices,
      (\alpha, \beta) and (\dot\alpha, \dot\beta), in this order.
    v70_from_sc8x8: Implements the inverse transform to v70_as_sc8x8.
    spin8_action_on_v70: [28, 70, 70]-array. For each spin(8) element
      in the 'vector' [i, j]-basis, provides the [70, 70] generator matrix
      when this generator acts on e7(7) / su(8). CAUTION: the output vector
      space's basis is dual (w.r.t. Killing form) to the input vector space's.
      This is useful for determining so(8)-invariant directions, but for
      computing mass matrices, spin8_action_on_v70o is much more appropriate.
    ijkl35: Tuple of all 35 lexicographically ordered 4-tuples of the form
      (0, j, k, l).
    fano_ijkl: Tuple of seven (i,j,k,l)-tuples that correspond to the quartic
      terms in (7.5) of arXiv:1909.10969 (with indexes shifted down to start
      from 0 rather than 1).
    sl2x7_88s: [7, 8, 8]-array, seven symmetric (8s x 8s) matrices that
      represent the 35s-components of seven commuting SL(2)s inside E7.
    sl2x7_88c: [7, 8, 8]-array, seven symmetric (8c x 8c) matrices that
      represent the 35c-components of seven commuting SL(2)s inside E7.
    sl2x7_88v: [7, 8, 8]-array, seven symmetric (8v x 8v) matrices that
      represent the 35v-components of seven commuting SL(2)s inside E7.
    sl2x7: [3, 7, 133]-array, indexed as [scv, num_sl2, e7_ad],
      generators for seven commuting SL(2)s inside E7. Leading index picks
      the (spinor, cospinor, vector)-35-irrep, middle index picks the
      specific SL(2), last index gives the e7-adjoint generator index.
      Indexing of the SL(2)s is in alignment with sl2x7_88{s,c,v}.
    v70_from_v70o: [70, 70]-array that maps orthonormal-basis-70-vectors to
      'common basis' 70-vectors.
      Commonly used as: v70 = v70_from_v70o.dot(v70o).
    v70o_from_v70: The inverse mapping of the above.
      Commonly used as: v70o = v70o_from_v70odot(v70).
    v133_from_v133o: [133, 133]-array that maps orthonormal-basis-133-vectors
      to 'common basis' 133-vectors.
    v133o_from_v133: The inverse mapping of the above.
    spin8_action_on_v70o: [28, 70, 70]-array. Like spin8_action_on_v70o,
      but with 70-vectors in the orthonormal basis.
    S_cr: [56, 56]-matrix that translates a "real sl(8)-basis"-index on the
      right to a "complex su(8)-basis" index on the left.
    S_rc: The inverse of S_cr.
    omega: [56, 56]-array, the symplectic E7 invariant.
    f_abc: [133, 133, 133]-array. The e7 structure constants with three
       adjoint indices.
    f_abC: [133, 133, 133]-array. The e7 structure constants with the last index
      in the co-adjoint representation.
    fo_abC: [133, 133, 133]-array. The e7 structure constants,
      using the orthonormal adjoint and co-adjoint basis.
    spin8: The Spin8 instance that was used to define E7.
    su8: The SU8 instance that was used to define E7.
  """

  def __init__(self, spin8, su8):
    """Initializes the instance.

    Args:
      spin8: The spin8 algebra to base this on. (May use varying conventions.)
      su8: The su(8) algebra to base this on.
    """
    self.spin8 = spin8
    self.su8 = su8
    t_a_ij_kl = numpy.zeros([133, 56, 56], dtype=numpy.complex128)
    t_a_ij_kl[:35, 28:, :28] = (1 / 8.0) * (
        nsum('ijklsS,qsS,Iij,Kkl->qIK',
             spin8.gamma_vvvvss, su8.m_35_8_8, su8.m_28_8_8, su8.m_28_8_8))
    t_a_ij_kl[:35, :28, 28:] = t_a_ij_kl[:35, 28:, :28]
    t_a_ij_kl[35:70, 28:, :28] = (1.0j / 8.0) * (
        nsum('ijklcC,qcC,Iij,Kkl->qIK',
             spin8.gamma_vvvvcc, su8.m_35_8_8, su8.m_28_8_8, su8.m_28_8_8))
    t_a_ij_kl[35:70, :28, 28:] = -t_a_ij_kl[35:70, 28:, :28]
    #
    # We need to find the action of the su(8) algebra on the
    # 28-representation.
    su8_28 = 2 * nsum('aij,mn,Iim,Jjn->aIJ',
                      su8.t_aij,
                      numpy.eye(8, dtype=numpy.complex128),
                      su8.m_28_8_8, su8.m_28_8_8)
    t_a_ij_kl[70:, :28, :28] = su8_28
    t_a_ij_kl[70:, 28:, 28:] = su8_28.conjugate()
    self.t_a_ij_kl = t_a_ij_kl
    m_35_8_8 = su8.m_35_8_8.real
    # Note that, due to the way our conventions work, the entries of this
    # matrix are all multiples of 1/8.0 = 0.125, which is an
    # exactly-representable floating point number. So, we are good to use this
    # even in conjunction with high-accuracy numerics(!). However,
    # we first have to 'sanitize away' numerical noise.
    raw_inv_gramian70 = nsum('AB,ab->AaBb', numpy.eye(2),
                             su8.ginv35).reshape(70, 70)
    self.inv_gramian70 = numpy.round(raw_inv_gramian70 * 8) / 8
    assert numpy.allclose(raw_inv_gramian70, self.inv_gramian70)
    # Assert that we only see 'good exact' numbers that are multiples of 1/8
    # with nonnegative values up to 16/8 = 2.
    assert set(abs(x * 8)
               for x in self.inv_gramian70.reshape(-1)) <= set(range(17))
    self.v70_as_sc8x8 = nsum('sc,xab->sxcab',
                             numpy.eye(2),
                             m_35_8_8).reshape(70, 2, 8, 8)
    self.v70_from_sc8x8 = nsum('vsab,vw->wsab',
                               self.v70_as_sc8x8,
                               self.inv_gramian70)
    # We also want to directly look at the action of the 28 Spin(8) generators
    # on the 70 scalars, both to determine residual gauge groups
    # (which we could also do in a 56-representation of E7),
    # and also to look for residual discrete subgroups of SO(8).
    spin8_action_on_s = 0.5 * nsum(
        'Aij,ijab->Aab', su8.m_28_8_8, spin8.gamma_vvss)
    spin8_action_on_c = 0.5 * nsum(
        'Aij,ijab->Aab', su8.m_28_8_8, spin8.gamma_vvcc)
    spin8_action_on_35s = (
        # [A,v,m,n]-array showing how acting with spin(8) generator A
        # changes a 35s element indexed by v, but with the change
        # expressed as a symmetric 8x8 matrix indexed (m, n).
        #
        # This could be simplified, exploiting symmetry, at the cost
        # of making the expression slightly less readable.
        nsum('Aab,van->Avbn', spin8_action_on_s,
             self.v70_as_sc8x8[:35, 0, :, :]) +
        nsum('Aab,vma->Avmb', spin8_action_on_s,
             self.v70_as_sc8x8[:35, 0, :, :]))
    spin8_action_on_35c = (
        # This could be simplified, exploiting symmetry, at the cost
        # of making the expression slightly less readable.
        nsum('Aab,van->Avbn', spin8_action_on_c,
             self.v70_as_sc8x8[35:, 1, :, :]) +
        nsum('Aab,vma->Avmb', spin8_action_on_c,
             self.v70_as_sc8x8[35:, 1, :, :]))
    spin8_action_on_35s35c = numpy.stack([spin8_action_on_35s,
                                          spin8_action_on_35c],
                                         axis=1)
    self.spin8_action_on_v70 = nsum(
        'Asvab,wsab->Asvw',
        spin8_action_on_35s35c,
        self.v70_from_sc8x8).reshape(28, 70, 70)
    # We also need an orthonormal basis for the 70 scalars.
    #
    # While we can find mass-eigenstates with the non-orthonormal basis
    # above (exercising a bit of care), these would be the eigenvalues of
    # a non-hermitean matrix operator. We do need orthonormal bases for
    # the mass-eigenstate subspaces so that subsequent automatic numerical
    # identification of charges can work (for which the code assumes that
    # charge-operators are represented as hermitean matrices, on which it
    # uses scipy.linalg.eigh() to produce orthonormal eigenbases).
    # We do not have to pay attention to define the mapping between these
    # 70-bases in a particularly elegant way.
    #
    # Also, it is important for high-accuracy calculations to have
    # exactly-representable matrix entries, while we can absorb an overall
    # (not-exactly-representable-at-finite-accuracy)
    # factor into the definition of the inner product.
    #
    # Furthermore, it is useful to have an orthonormal basis
    # for 56x133 Theta-tensors available. The primary reason here is that
    # we want to express things such as 'the projection to the linear
    # 912-constraint' in terms of proper coordinate-orthonormal bases.
    # If we used a non-orthonormal basis instead, this would lead to such
    # awkward situations as some important linear operations, such as
    # some projections, being conceptually expressible in terms of
    # symmetric matrices, but not appearing in that form - and consequently,
    # tf/numpy eigh() functions not being able to give us an orthonormal
    # eigenbasis (The corresponding .eig() functions do not give us
    # orthonormal bases and are numerically rather problematic to use).
    # So, we do form a "pseudoeuclidean-orthonormal" basis for the
    # entire 133-dimensional algebra for which the inner product
    # tr (g_a g_b) is delta_ab * {constant} * (+1 or -1).
    self.ijkl35 = tuple(ijkl for ijkl in itertools.combinations(range(8), 4)
                        if 0 in ijkl)
    v133_from_v133o = numpy.zeros([133, 133])
    # The SO(8) part stays unchanged.
    v133_from_v133o[-28:, -28:] = numpy.eye(28)
    for num_ijkl, ijkl in enumerate(self.ijkl35):
      v35s = nsum('mab,mM,ab->M', su8.m_35_8_8,
                  su8.ginv35,
                  spin8.gamma_vvvvss[ijkl[0], ijkl[1], ijkl[2], ijkl[3], :, :])
      v35c = nsum(
          'mab,mM,ab->M',
          su8.m_35_8_8, su8.ginv35,
          spin8.gamma_vvvvcc[ijkl[0], ijkl[1], ijkl[2], ijkl[3], :, :])
      v35v = nsum(
          'mab,mM,ab->M',
          su8.m_35_8_8, su8.ginv35,
          spin8.gamma_ssssvv[ijkl[0], ijkl[1], ijkl[2], ijkl[3], :, :])
      v133_from_v133o[0:35, num_ijkl] = 0.5 * v35s
      v133_from_v133o[35:70, 35 + num_ijkl] = 0.5 * v35c
      v133_from_v133o[70:105, 70 + num_ijkl] = 0.5 * v35v
    v70_from_v70o = v133_from_v133o[:70, :70]
    assert numpy.allclose(
        nsum('Vv,Wv->VW', v70_from_v70o, v70_from_v70o),
        2 * self.inv_gramian70)
    self.v133_from_v133o = v133_from_v133o
    self.v133o_from_v133 = numpy.linalg.inv(v133_from_v133o)
    #
    self.v70_from_v70o = v133_from_v133o[:70, :70]
    self.v70o_from_v70 = self.v133o_from_v133[:70, :70]
    self.spin8_action_on_v70o = nsum(
        'aVw,Ww->aVW',
        nsum('avw,vV->aVw',
             self.spin8_action_on_v70,
             self.v70_from_v70o),
        self.v70o_from_v70)
    #
    # Translating between the 'real' and 'complex' e7(7) basis.
    #
    self.S_cr = nsum('ab,AB->aAbB',
                     numpy.array([[1.0, 1.0j], [1.0, -1.0j]]),
                     numpy.eye(28)).reshape(56, 56) / 2**.5
    self.S_rc = numpy.linalg.inv(self.S_cr)
    assert numpy.allclose(self.S_rc @ self.S_cr, numpy.eye(56))
    #
    # The symplectic invariant of E7.
    #
    self.omega = nsum('ab,AB->aAbB',
                      numpy.array([[0.0, 1.0], [-1.0, 0.0]]),
                      numpy.eye(28)).reshape(56, 56)
    fano_quartets = Spin8.FANO_QUARTETS
    self.fano_ijkl = fano_ijkl = tuple(
        tuple(int(x) - 1 for x in xs)
        for xs in fano_quartets.split())
    hamming_factors = numpy.array(
        [-1, 1, 1, -1, 1, -1, -1])
    #
    # The 'seven commuting SL(2)s' submanifold.
    #
    self.sl2x7_88s = -numpy.stack([spin8.gamma_vvvvss[ijkl]
                                   for ijkl in fano_ijkl], axis=0)
    self.sl2x7_88c = -numpy.stack(
        [spin8.gamma_vvvvcc[ijkl]
         for ijkl in fano_ijkl], axis=0) * (
             hamming_factors[:, numpy.newaxis, numpy.newaxis])
    self.sl2x7_88v = numpy.stack(
        [numpy.diag([(+1.0, -1.0)[int(n in ijkl)]
                     for n in range(8)])
         for ijkl in fano_ijkl], axis=0) * (
             hamming_factors[:, numpy.newaxis, numpy.newaxis])
    def get_35s_from_88(n88):
      return 0.5 * nsum('nab,Aab,BA->nB',
                        n88, su8.m_35_8_8, su8.ginv35)
    sl2x7_35s = get_35s_from_88(self.sl2x7_88s)
    sl2x7_35c = get_35s_from_88(self.sl2x7_88c)
    sl2x7_35v = get_35s_from_88(self.sl2x7_88v)
    self.sl2x7 = numpy.stack(
        [numpy.pad(sl2x7_35s, [(0, 0), (0, 35 * 2 + 28)]),
         numpy.pad(sl2x7_35c, [(0, 0), (35, 35 + 28)]),
         numpy.pad(sl2x7_35v, [(0, 0), (35 * 2, 28)])],
        axis=0)

  def v70_from_35s35c(self, m35s, m35c):
    """Computes a v70-vector from 35s and 35c matrices."""
    return nsum('vsab,sab->v',
                self.v70_from_sc8x8,
                numpy.stack([m35s, m35c])).real

  def v70_as_35s35c(self, v70):
    m = nsum('v,vsab->sab', v70, self.v70_as_sc8x8).real
    return m[0], m[1]

  def get_cartan_subalgebra(self, conventions='SO8-SU8'):
    """Returns a Cartan Subalgebra of E7.

    The Cartan Subalgebra is embedded into the real E7 algebra,
    so if it consists of compact ("antisymmetric") generators,
    it lacks a factor 1j.

    Args:
      conventions: The conventions to use.
        Currently, this supports:
        - 'SO8-SU8': An all-compact-generators Cartan subalgebra
          whose first four generators form a Cartan subalgebra of
          SO(8)-in-E7.
        - 'SU8-diag7': A Cartan subalgebra consisting of seven
          commuting diag(0, 0, ..., 1, -1, 0, ..., 0) generators
          in 35v. These are not orthonormal w.r.t. the Killing form of E7.
        - 'SU8-fano': Seven noncompact generators from 35v that are in
          one-to-one alignment with lines on the Fano plane by
          taking the gamma_ssssvv elements of the 4-forms that are word-dual
          to the 3 indices on each line.
          If E7 is built with octionionic Spin(8)-conventions,
          this gives a particularly nice form of (SU(1,1)/U(1))^7.

    Returns:
      A float [7, 133]-ndarray of Cartan subalgebra generators.
    """
    su8 = self.su8
    inv_ij_map = su8.inv_ij_map
    cartan_gens = numpy.zeros([7, 133])
    if conventions == 'SO8-SU8':
      # The first four Cartan generators are the
      # [0, 1], [2, 3], [4, 5], [6, 7]-rotations of SO(8).
      # Overall normalization is such that Cartan-generators
      # have eigenvalues 1j * {-1, -0.5, 0, 0.5, 1}.
      for n in range(4):
        cartan_gens[n, 105 + inv_ij_map[(2 * n, 2 * n + 1)]] = 0.5
      # The next three Cartan generators must be identity matrices
      # on each [i, i+1]-subspace on which one of the first four
      # generators implements a SO(2)-rotation.
      diagonals = [[+1, +1, +1, +1, -1, -1, -1, -1],
                   [+1, +1, -1, -1, +1, +1, -1, -1],
                   [+1, +1, -1, -1, -1, -1, +1, +1]]
      # Scalar products with the [0, ..., 0, +1, -1, 0, ..., 0]
      # matrices:
      sprods = numpy.array([
          [diagonal[n] - diagonal[n + 1] for n in range(7)]
          for diagonal in diagonals])  # [3, 7]-array.
      # The inner products of the relevant 7 diagonal matrices
      # are available from self.inv_gramian70.
      cartan_gens[4:, 70:77] = nsum(
          'ab,na->nb',
          self.inv_gramian70[:7, :7],
          0.25 * sprods)
      return cartan_gens
    elif conventions == 'SU8-diag7':
      # This choice gives roots such as:
      # [ 0., -1.,  2., -1.,  0.,  0.,  0.]
      # [ 0., -0., -1., -0.,  0., -0.,  1.]
      for n in range(7):
        cartan_gens[n, 70 + n] = 0.5
      return cartan_gens
    elif conventions == 'SU8-fano':
      # Orthonormal choice in the '35v'.
      # Strategy: Out of the self-dual 4-forms over the spinors [abcd],
      # pick seven according to their complement being a
      # Fano-Plane-triplet.
      fano_lines = self.spin8.FANO_LINES
      fano_triplets = set(tuple(int(d) - 1 for d in ijk)
                          for ijk in fano_lines.split())
      abcds = [p7[3:] for p7 in itertools.permutations(range(7))
               if p7[:3] in fano_triplets and list(p7[3:]) == sorted(p7[3:])]
      for n, abcd in enumerate(abcds):
        v88 = self.spin8.gamma_ssssvv[abcd]
        # TODO(tfish): inv_gramian70[:35, :35] should also be available
        # separately.
        v35 = nsum('Aab,ab,BA->B',
                   self.su8.m_35_8_8, v88, self.inv_gramian70[:35, :35])
        cartan_gens[n, 70:105] = v35 * 0.25
      assert numpy.allclose(
          0, nsum('ma,nb,abC->mnC', cartan_gens, cartan_gens, self.f_abC)), (
              'Cartan-generators do not commute.')
      return cartan_gens

  def get_root_op(self, cartan_subalgebra, root):
    """Gets an E7 root-operator."""
    rs_plus = mu.get_simultaneous_eigenspace(
        (-1j) * nsum('na,abC->nCb', cartan_subalgebra, self.f_abC),
        root)
    if len(rs_plus) != 1:
      raise ValueError('Rootspace is not one-dimensional: root=%r' % root)
    # Subtlety: The eigenvector of a C^n -> C^n mapping, even if we
    # length-normalize it to 1, is only determined up to a complex phase -
    # and what we get may actually well depend on the eigenvalue library
    # implementation. So, we have to uniquely fix this phase here.
    # The most straightforward choice is to make the lexically-first
    # nonzero entry real-and-positive. While determining the 'lexically first'
    # vector entry of a numerically noisy vector is an ill-defined procedure,
    # we here can get away with it as long as our Cartan subalgebra choice
    # is reasonably nice.
    key_index, _ = next((n, c) for n, c in enumerate(rs_plus[0].round(7))
                        if abs(c))
    r_plus = rs_plus[0] * numpy.exp(-1j * numpy.log(rs_plus[0][key_index]).imag)
    r_minus = r_plus.conj()
    comm = nsum('abC,a,b->C', self.f_abC, r_plus, r_minus)
    # The commutator should be an element of the Cartan subalgebra (CSA).
    # We always pick the CSA such that it consists of all-compact or
    # all-noncompact generators, hence we normalize the magnitude of the root
    # such that it gives us an element of fixed-magnitude length-squared.
    #
    normalization = abs(
        nsum('AB,A,B->', (self.k133 / self.k133[-1, -1]), comm, comm))**.5 / 2.0
    return r_plus / normalization**.5

  @cached_property.cached_property
  def t56r(self):
    """The real "SL(8)-basis" e7(7) generator matrices."""
    # This only gets computed when actually needed.
    ret = nsum(
        'aRJ,JS->aRS',
        nsum('aIJ,IR->aRJ', self.t_a_ij_kl, self.S_cr),
        self.S_cr.conj())
    assert numpy.allclose(ret, ret.real)
    return ret.real

  @cached_property.cached_property
  def _f_abc_f_abC(self):
    # We here use the (smaller) real generator matrices.
    return mu.get_f_abc(self.t56r)

  @property
  def f_abc(self):
    return self._f_abc_f_abC[0]

  @property
  def f_abC(self):
    return self._f_abc_f_abC[1]

  @cached_property.cached_property
  def fo_abC(self):
    return nsum('abC,aA,bB,cC->ABc',
                self.f_abC,
                self.v133_from_v133o,
                self.v133_from_v133o,
                self.v133o_from_v133)

  @cached_property.cached_property
  def k133(self):
    return numpy.einsum('aBC,bCB->ab', self.f_abC, self.f_abC)

class E8:
  """Container class for e8 tensor invariants."""

  def __init__(self, spin8):
    self.spin8 = spin8
    gamma_vsc = spin8.gamma_vsc
    I_J_by_IJ = [(i, j) for i in range(16) for j in range(16) if i < j]
    IJ_by_I_J = {k: n for n, k in enumerate(I_J_by_IJ)}
    def paired(I, J):
      return (I, J) if I < J else (J, I)
    H_IJ_I_J = numpy.zeros([120, 16, 16], dtype=numpy.float64)
    for IJ, (I, J) in enumerate(I_J_by_IJ):
      H_IJ_I_J[IJ, I, J] = +0.5
      H_IJ_I_J[IJ, J, I] = -0.5
    gamma16_vsc = numpy.zeros([16, 128, 128], dtype=numpy.float64)
    gamma16_vsc[:8, :64, :64] = nsum(
        'ac,ibd->iabcd', numpy.eye(8), gamma_vsc).reshape(8, 64, 64)
    gamma16_vsc[:8, 64:, 64:] = nsum(
        'ac,idb->iabcd', numpy.eye(8), gamma_vsc).reshape(8, 64, 64)
    gamma16_vsc[8:, :64, 64:] = nsum(
        'bd,iac->iabcd', numpy.eye(8), gamma_vsc).reshape(8, 64, 64)
    gamma16_vsc[8:, 64:, :64] = -nsum(
        'bd,ica->iabcd', numpy.eye(8), gamma_vsc).reshape(8, 64, 64)
    #
    gamma16_vvss = mu.asymm2(
        nsum('IAC,JBC->IJAB', gamma16_vsc, gamma16_vsc), 'IJAB->JIAB')
    gamma16_IJ_A_B = nsum('IJAB,MIJ->MAB', gamma16_vvss, H_IJ_I_J)
    #
    f_ABC = numpy.zeros([248, 248, 248], dtype=numpy.float64)
    #
    def s(a, b, a_lt_b, a_gt_b):
      if a < b:
        return a_lt_b
      if a > b:
        return a_gt_b
      return 0.0
    for IJ, (I, J) in enumerate(I_J_by_IJ):
      # The IJ-rotation...
      for MN, (M, N) in enumerate(I_J_by_IJ):
        # ...when acting on MN, needs to have J=M, and produces IN,
        if J == M and I != N:
          f_ABC[128 + IJ,
                128 + MN,
                128 + IJ_by_I_J[paired(I, N)]] += s(I, N, -1, 1)
        # ...or needs to have J=N, and produces -IM,
        if J == N and I != M:
          f_ABC[128 + IJ,
                128 + MN,
                128 + IJ_by_I_J[paired(I, M)]] += s(I, M, +1, -1)
        # Or the negatives, for I<->J.
        if I == M and J != N:
          f_ABC[128 + IJ,
                128 + MN,
                128 + IJ_by_I_J[paired(J, N)]] += s(J, N, +1, -1)
        if I == N and J != M:
          f_ABC[128 + IJ,
                128 + MN,
                128 + IJ_by_I_J[paired(J, M)]] += s(J, M, -1, +1)
    f_ABC[128:, :128, :128] = +0.5 * gamma16_IJ_A_B
    f_ABC[:128, 128:, :128] = +0.5 * nsum('MAB->BMA', gamma16_IJ_A_B)
    f_ABC[:128, :128, 128:] = -0.5 * nsum('MAB->ABM', gamma16_IJ_A_B)
    self.I_J_by_IJ = I_J_by_IJ
    self.IJ_by_I_J = IJ_by_I_J
    self.gamma16_vsc = gamma16_vsc
    self.H_IJ_I_J = H_IJ_I_J
    self.f_ABC = f_ABC
    #
    self.spin16_I_J_CB = nsum(
        'ABC,AIJ->IJCB',
        f_ABC,
        numpy.pad(H_IJ_I_J, ((128, 0), (0, 0), (0, 0))))

class G:
  """Wrapper class for lazy-instantiation of only actually-used algebra definitions."""

  @cached_property.cached_property
  def spin8(self):
    return Spin8()

  @cached_property.cached_property
  def su8(self):
    return SU8()

  @cached_property.cached_property
  def e7(self):
    return E7(self.spin8, self.su8)

  @cached_property.cached_property
  def e8(self):
    return E8(self.spin8)

g = G()


text:
/**
 * Authorization middleware
 *
 * @param   {String}   role   Role for protected route
 * @returns {void}
 */
module.exports = (role) => async (ctx, next) => {
  const { roles } = ctx.state;
  const allowed = roles.includes(role);
  if (allowed) {
    await next();
    return;
  }
  ctx.status = 403;
};

redpajama_set_name: RedPajamaGithub


text:
import Rx from 'rx';
import objectUtils from '../util/object';

// TODO: remove event listeners

const bind = ( GameClock, keyActions ) => {
    // Lookup of key codes to timer ID, or null for no repeat
    //
    const actionIds = {};

    // When key is pressed and we don't already think it's pressed, call the
    // key action callback and set a timer to generate another one after a delay
    //
    document.onkeydown = ( event ) => {
        const key = objectUtils.getSafeKey( event.key || event.which );
        const keyAction = keyActions[key];

        if ( !keyAction || typeof keyAction.action !== 'function' ) {
          return;
        }

        if ( keyAction.preventDefault ) {
          event.preventDefault();
        }

        if ( keyAction.onceOnly ) {
          GameClock.addAction( keyAction.action, keyAction.onceOnly, keyAction.alwaysRun );
          return;
        }

        if ( !actionIds[key] ) {
          actionIds[key] = GameClock.addAction( keyAction.action );
        }
    };

    // Cancel timeout and mark key as released on keyup
    //
    document.onkeyup = ( event ) => {
        const key = objectUtils.getSafeKey( event.key || event.which );
        if ( actionIds[key] ) {
          event.preventDefault();
          GameClock.removeAction( actionIds[key] );
          delete actionIds[key];
        }
    };

    // When window is unfocused we may not get key events. To prevent this
    // causing a key to 'get stuck down', cancel all held keys
    //
    window.onblur = ( event ) => {

      Object.keys( actionIds ).forEach( (key) => {
        if ( actionIds[key] !== null ) {
          GameClock.removeAction( actionIds[key] );
        }
        delete actionIds[key];
      });
    };
};

const KeyboardControls = {
  bind: bind
}

export default KeyboardControls;


text:
package opencup.xiv.round4;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class J {

    int[] tt = null;
    int tx = 0;
    private BufferedReader reader;
    private PrintWriter writer;

    public J() throws IOException {
        reader = new BufferedReader(new InputStreamReader(System.in));
        writer = new PrintWriter(System.out);
    }

    public static void main(String[] args) throws IOException {
        final J j = new J();
        j.fastSolve();
    }

    int[] readInts() throws IOException {
        String[] s = reader.readLine().split(" ");
        int[] ints = new int[s.length];
        for (int i = 0; i < ints.length; i++) {
            ints[i] = Integer.parseInt(s[i]);
        }
        return ints;
    }

    int readInt() throws IOException {
        if (tt == null || tx == tt.length) {
            tt = readInts();
            tx = 0;
        }
        return tt[tx++];
    }

    private void fastSolve() throws IOException {
        int n = readInt();
        long ans = 0;
        for (int i = 1; i <= n; i *= 2) {
            ans += 1L * (i - 1) * i / 2;
            n -= i;
        }
        ans += 1L * (n - 1) * n / 2;
        writer.println(ans);
        writer.flush();
    }

}


text:
<?php

use skeeks\cms\mail\helpers\Html;

/* @var $this yii\web\View */
/* @var $model \skeeks\cms\shop\models\ShopQuantityNoticeEmail */
?>

<?= Html::beginTag('h1'); ?>
<?= \Yii::t('skeeks/shop/app', "We've got the goods interesting you"); ?>!
<?= Html::endTag('h1'); ?>

<?= Html::tag('hr'); ?>

<?= Html::beginTag('h3'); ?>
<?= \Yii::t('skeeks/shop/app', 'Product'); ?>:
<?= Html::endTag('h3'); ?>

<?= Html::beginTag('p'); ?>
<?=
\yii\grid\GridView::widget([
    'dataProvider' => new \yii\data\ArrayDataProvider([
        'allModels' => $model->getShopProduct()->all(),
    ]),
    'filterModel'  => null,
    'filterUrl'    => \yii\helpers\Url::to('/', true),
    'layout'       => "{items}",
    'columns'      =>
        [

            [
                'class'  => \yii\grid\DataColumn::class,
                'format' => 'raw',
                'value'  => function (\skeeks\cms\shop\models\ShopProduct $shopProduct) {
                    if ($shopProduct->cmsContentElement->image) {
                        return Html::a(
                            Html::img($shopProduct->cmsContentElement->image->absoluteSrc, ['width' => 80])
                            , $shopProduct->cmsContentElement->absoluteUrl, [
                            'target'    => '_blank',
                            'titla'     => "Смотреть на сайте",
                            'data-pjax' => 0,
                        ]);
                    }
                },
            ],
            [
                'class'  => \yii\grid\DataColumn::class,
                'label'  => \Yii::t('skeeks/shop/app', 'Product name'),
                'format' => 'raw',
                'value'  => function (\skeeks\cms\shop\models\ShopProduct $shopProduct) {
                    if ($shopProduct->cmsContentElement->absoluteUrl) {
                        return Html::a($shopProduct->cmsContentElement->name,
                            $shopProduct->cmsContentElement->absoluteUrl, [
                                'target'    => '_blank',
                                'titla'     => "Смотреть на сайте",
                                'data-pjax' => 0,
                            ]);
                    } else {
                        return $shopProduct->cmsContentElement->name;
                    }

                },
            ],

            [
                'class'     => \yii\grid\DataColumn::class,
                'attribute' => 'quantity',
                'value'     => function (\skeeks\cms\shop\models\ShopProduct $shopProduct) {
                    return $shopProduct->quantity." ".$shopProduct->measure->symbol;
                },
            ],

            [
                'class'     => \yii\grid\DataColumn::class,
                'label'     => \Yii::t('skeeks/shop/app', 'Price'),
                'attribute' => 'price',
                'format'    => 'raw',
                'value'     => function (\skeeks\cms\shop\models\ShopProduct $shopProduct) {
                    return (string)$shopProduct->baseProductPrice->money."<br />";
                },
            ],
        ],
])
?>
<?= Html::endTag('p'); ?>


text:
using NPOI.HPSF;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using HuaGong.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;

namespace HuaGong.Utils
{
    public class ReportHelper
    {
        static HSSFWorkbook workbook;

        //public static void ExportRoomBills(IEnumerable<RoomBill> bills, string[] titles, string fileName)
        //{
        //    workbook = new HSSFWorkbook();
        //    InitializeWorkbook();
        //    ISheet sheet = workbook.CreateSheet("能耗缴费历史账单");
        //    //SetPrintSetting(sheet); // 设置打印格式
        //    SetHeaderTitle(sheet, titles); // 设置标题栏
        //    int rowIndex = 0;
        //    for (int i = 0; i < bills.Count(); i++)
        //    {
        //        RoomBill bill = bills.ElementAt(i);
        //        int deviceCount = bill.DeviceBills.Count();
        //        IRow row = CreateCommonRow(sheet, ++rowIndex);
        //        // 房间编号
        //        CreateCommonCell(sheet, row, 0, bill.RoomNo);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 0, 0));
        //        // 结算批号
        //        CreateCommonCell(sheet, row, 1, bill.LotNo);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 1, 1));
        //        // 业主姓名
        //        CreateCommonCell(sheet, row, 2, bill.RealName);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 2, 2));
        //        // 结账时间
        //        CreateCommonCell(sheet, row, 3, bill.DateTime.ToString());
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 3, 3));
        //        // 总费用
        //        CreateCommonCell(sheet, row, 10, bill.SumMoney.ToString());
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 10, 10));
        //        // 账户余额
        //        CreateCommonCell(sheet, row, 11, bill.BilledAccountBalance);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 11, 11));
        //        for (int j = 0; j < deviceCount; j++)
        //        {
        //            DeviceBill deviceBill = bill.DeviceBills.ElementAt(j);
        //            CreateCommonCell(sheet, row, 4, deviceBill.DeviceNo);
        //            CreateCommonCell(sheet, row, 5, deviceBill.DeviceName);
        //            CreateCommonCell(sheet, row, 6, deviceBill.PreValue.ToString());
        //            CreateCommonCell(sheet, row, 7, deviceBill.CurValue.ToString());
        //            CreateCommonCell(sheet, row, 8, deviceBill.Price);
        //            CreateCommonCell(sheet, row, 9, deviceBill.Money);
        //            if (j != deviceCount - 1)
        //                row = CreateCommonRow(sheet, ++rowIndex);
        //        }
        //    }

        //    WriteToFile(fileName);
        //}

        //public static void ExportPrepaidBills(IEnumerable<PrepaidBill> prepaidBills, string[] titles, string fileName)
        //{
        //    workbook = new HSSFWorkbook();
        //    InitializeWorkbook();
        //    ISheet sheet = workbook.CreateSheet("能耗预付费实时账单");
        //    //SetPrintSetting(sheet); // 设置打印格式
        //    SetHeaderTitle(sheet, titles); // 设置标题栏
        //    int rowIndex = 0;
        //    for (int i = 0; i < prepaidBills.Count(); i++)
        //    {
        //        PrepaidBill bill = prepaidBills.ElementAt(i);
        //        int deviceCount = bill.PrepaidDeviceBills.Count();
        //        IRow row = CreateCommonRow(sheet, ++rowIndex);
        //        // 房间编号
        //        CreateCommonCell(sheet, row, 0, bill.RoomNo);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 0, 0));
        //        // 建筑编号
        //        CreateCommonCell(sheet, row, 1, bill.BuildingNo);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 1, 1));
        //        // 业主姓名
        //        CreateCommonCell(sheet, row, 2, bill.RealName);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 2, 2));
        //        // 总价格
        //        CreateCommonCell(sheet, row, 9, bill.SumMoney);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 9, 9));
        //        // 账户余额
        //        CreateCommonCell(sheet, row, 10, bill.AccountBalance);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 10, 10));
        //        // 结算余额
        //        CreateCommonCell(sheet, row, 11, bill.BilledBalance);
        //        sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex + deviceCount - 1, 11, 11));
        //        for (int j = 0; j < deviceCount; j++)
        //        {
        //            PrepaidDeviceBill deviceBill = bill.PrepaidDeviceBills.ElementAt(j);
        //            CreateCommonCell(sheet, row, 3, deviceBill.DeviceNo);
        //            CreateCommonCell(sheet, row, 4, deviceBill.DeviceName);
        //            CreateCommonCell(sheet, row, 5, deviceBill.PreValue.ToString());
        //            CreateCommonCell(sheet, row, 6, deviceBill.CurValue.ToString());
        //            CreateCommonCell(sheet, row, 7, deviceBill.Price);
        //            CreateCommonCell(sheet, row, 8, deviceBill.Money);
        //            if (j != deviceCount - 1)
        //                row = CreateCommonRow(sheet, ++rowIndex);
        //        }
        //    }

        //    WriteToFile(fileName);
        //}

        public static void Export<T>(Dictionary<string, string> pairs, IEnumerable<T> sources, string fileName)
        {
            string sheetName = fileName.Substring(0, fileName.LastIndexOf('.'));
            string[] titles = pairs.Keys.ToArray();
            string[] properties = pairs.Values.ToArray();
            workbook = new HSSFWorkbook();
            InitializeWorkbook();
            ISheet sheet = workbook.CreateSheet(sheetName);
            //SetPrintSetting(sheet); // 设置打印格式
            SetHeaderTitle(sheet, titles); // 设置标题栏
            for (int i = 0; i < sources.Count(); i++)
            {
                T item = sources.ElementAt(i);
                IRow row = CreateCommonRow(sheet, i + 1);
                for (int j = 0; j < properties.Length; j++)
                {
                    var propertyInfo = item.GetType().GetProperty(properties[j]);
                    object obj = propertyInfo.GetValue(item);
                    string value = string.Empty;
                    if (obj != null)
                        value = obj.ToString();
                    CreateCommonCell(sheet, row, j, value);
                }
            }

            WriteToFile(fileName);
        }

        private static void SetHeaderTitle(ISheet sheet, string[] titles)
        {
            IRow row = sheet.CreateRow(0);
            row.HeightInPoints = 30;
            for (int i = 0; i < titles.Length; i++)
            {
                ICell cell = row.CreateCell(i);
                cell.SetCellValue(titles[i]);
                sheet.AutoSizeColumn(i);

                ICellStyle style = workbook.CreateCellStyle();
                style.Alignment = HorizontalAlignment.Center;
                style.VerticalAlignment = VerticalAlignment.Center;
                IFont font = workbook.CreateFont();
                font.IsBold = true;
                style.SetFont(font);
                cell.CellStyle = style;
            }
        }

        private static IRow CreateCommonRow(ISheet sheet, int rowIndex)
        {
            IRow row = sheet.CreateRow(rowIndex);
            row.HeightInPoints = 20;
            return row;
        }

        private static void CreateCommonCell(ISheet sheet, IRow row, int index, string value)
        {
            sheet.AutoSizeColumn(index);
            ICell cell = row.CreateCell(index);
            cell.SetCellValue(value);
            ICellStyle style = workbook.CreateCellStyle();
            style.Alignment = HorizontalAlignment.Center;
            style.VerticalAlignment = VerticalAlignment.Center;
            cell.CellStyle = style;
        }

        private static void InitializeWorkbook()
        {
            //Create a entry of DocumentSummaryInformation
            DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation();
            dsi.Company = "华南理工大学";
            workbook.DocumentSummaryInformation = dsi;

            //Create a entry of SummaryInformation
            SummaryInformation si = PropertySetFactory.CreateSummaryInformation();
            si.Subject = "智慧能源系统";
            workbook.SummaryInformation = si;
        }

        private static void SetPrintSetting(ISheet sheet)
        {
            sheet.SetMargin(MarginType.RightMargin, (double)0.5);
            sheet.SetMargin(MarginType.TopMargin, (double)0.5);
            sheet.SetMargin(MarginType.LeftMargin, (double)0.5);
            sheet.SetMargin(MarginType.BottomMargin, (double)0.5);

            sheet.PrintSetup.Copies = 1;
            sheet.PrintSetup.NoColor = true;
            sheet.PrintSetup.Landscape = true;
            sheet.PrintSetup.PaperSize = (short)PaperSize.A4;

            sheet.FitToPage = true;
            sheet.PrintSetup.FitHeight = 2;
            sheet.PrintSetup.FitWidth = 3;
            sheet.IsPrintGridlines = true;
        }

        private static void WriteToFile(string fileName)
        {
            //Write the stream data of workbook to the root directory
            string path = AppDomain.CurrentDomain.BaseDirectory;
            path = Path.Combine(path, fileName);
            FileStream file = new FileStream(path, FileMode.Create);
            workbook.Write(file);
            file.Close();
        }
    }
}


text:
package cc.suitalk.ipcinvoker.inner;

import android.os.Bundle;

/**
 * Created by albieliang on 2017/6/18.
 */

public interface IPCData {

    Bundle toBundle();

    void fromBundle(Bundle bundle);
}


text:
package com.jsh.dao.basic;

import com.jsh.base.BaseDAO;
import com.jsh.model.po.Basicuser;

public class UserDAO extends BaseDAO<Basicuser> implements UserIDAO
{
	/**
     * 设置dao映射基类
     * @return
     */
	@Override
	public Class<Basicuser> getEntityClass()
	{
		return Basicuser.class;
	}

}


text:
package com.google.gerrit.server.schema;

import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;

import java.sql.SQLException;
import java.sql.Statement;

public class Schema_66 extends SchemaVersion {

  @Inject
  Schema_66(Provider<Schema_65> prior) {
    super(prior);
  }

  @Override
  protected void migrateData(ReviewDb db, UpdateUI ui)
      throws OrmException, SQLException {
    final Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
    try {
      stmt.executeUpdate("UPDATE accounts SET reverse_patch_set_order = 'Y' "+
                         "WHERE display_patch_sets_in_reverse_order = 'Y'");
      stmt.executeUpdate("UPDATE accounts SET show_username_in_review_category = 'Y' " +
                         "WHERE display_person_name_in_review_category = 'Y'");
    } finally {
      stmt.close();
    }
  }
}


text:
#include "forward_renderer.h"

#include <cubez/render.h>
#include <cubez/mesh.h>
#include <map>
#include <iostream>
#include <array>
#include <assert.h>
#include <cglm/struct.h>

const size_t kMaxDirectionalLights = 4;
const size_t kMaxPointLights = 32;
const size_t kMaxSpotLights = 8;

struct LightPoint {
  // Interleave the brightness and radius to optimize std140 wasted bytes.
  vec3s rgb;
  float brightness;
  vec3s pos;
  float radius;
};

typedef struct LightDirectional {
  vec3s rgb;
  float brightness;
  vec3s dir;
  float _dir_pad;
};

typedef struct LightSpot {
  vec3s rgb;
  float brightness;
  vec3s pos;
  float range;
  vec3s dir;
  float angle_deg;
};

typedef struct qbForwardRenderer_ {
  qbRenderer_ renderer;

  qbRenderPass scene_3d_pass;
  qbRenderPass scene_2d_pass;
  qbRenderPass gui_pass;

  qbFrameBuffer frame_buffer;

  qbGeometryDescriptor supported_geometry = nullptr;

  qbSystem render_system;

  // The first binding of user textures.
  size_t texture_start_binding;
  size_t texture_units_count;

  // The first binding of user uniforms.
  size_t uniform_start_binding;
  size_t uniform_count;

  uint32_t camera_uniform;
  std::string camera_uniform_name;
  qbGpuBuffer camera_ubo;

  // Created per rendergroup.
  uint32_t model_uniform;
  std::string model_uniform_name;

  // Created per rendergroup.
  uint32_t material_uniform;
  std::string material_uniform_name;

  uint32_t light_uniform;
  std::string light_uniform_name;
  qbGpuBuffer light_ubo;

  std::array<struct LightDirectional, kMaxDirectionalLights> directional_lights;
  std::array<bool, kMaxDirectionalLights> enabled_directional_lights;

  std::array<struct LightPoint, kMaxPointLights> point_lights;
  std::array<bool, kMaxPointLights> enabled_point_lights;

  std::array<struct LightSpot, kMaxSpotLights> spot_lights;
  std::array<bool, kMaxSpotLights> enabled_spot_lights;

  size_t albedo_map_binding;
  size_t normal_map_binding;
  size_t metallic_map_binding;
  size_t roughness_map_binding;
  size_t ao_map_binding;
  size_t emission_map_binding;

  qbImage empty_albedo_map;
  qbImage empty_normal_map;
  qbImage empty_metallic_map;
  qbImage empty_roughness_map;
  qbImage empty_ao_map;
  qbImage empty_emission_map;

  qbSurface blur_surface;
  qbGpuBuffer blur_ubo;

  qbSurface merge_surface;
  qbGpuBuffer merge_ubo;

  // Voxel
  // https://0fps.net/category/programming/voxels/
  // https://forum.openframeworks.cc/t/gpu-marching-cubes-example/9129
  // https://github.com/chriskiefer/MarchingCubesGPU/blob/master/src/marchingCubesGPU.cpp
  // http://www.icare3d.org/codes-and-projects/codes/opengl_geometry_shader_marching_cubes.html
  // http://users.belgacom.net/gc610902/
  // https://github.com/dominikwodniok/dualmc
} qbForwardRenderer_, *qbForwardRenderer;

struct qbBlurArgs {
  int32_t horizontal;
};

struct qbMergeArgs {
  int32_t bloom;
  float exposure;
};

struct CameraUniform {
  alignas(16) mat4s vp;
};

struct ModelUniform {
  alignas(16) mat4s m;
  alignas(16) mat4s rot;
};

struct MaterialUniform {
  vec3s albedo;
  float metallic;
  vec3s emission;
  float roughness;
};

struct LightUniform {
  LightDirectional directionals[kMaxDirectionalLights];
  LightPoint points[kMaxPointLights];
  LightSpot spots[kMaxSpotLights];

  vec3s view_pos;
  float _view_pos_pad;
};

void rendergroup_oncreate(struct qbRenderer_* self, qbRenderGroup group) {
  qbForwardRenderer r = (qbForwardRenderer)self;

  qbGpuBuffer model;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = qbGpuBufferType::QB_GPU_BUFFER_TYPE_UNIFORM;
    attr.size = sizeof(ModelUniform);
    attr.name = r->model_uniform_name.data();
    qb_gpubuffer_create(&model, &attr);
  }

  qbGpuBuffer material;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = qbGpuBufferType::QB_GPU_BUFFER_TYPE_UNIFORM;
    attr.size = sizeof(ModelUniform);
    attr.name = r->material_uniform_name.data();
    qb_gpubuffer_create(&material, &attr);
  }

  uint32_t bindings[] = { ((qbForwardRenderer)self)->model_uniform,
                          ((qbForwardRenderer)self)->material_uniform };
  qbGpuBuffer uniforms[] = { model, material };
  qb_rendergroup_attachuniforms(group, sizeof(uniforms) / sizeof(qbGpuBuffer),
                                bindings, uniforms);
}

void rendergroup_ondestroy(struct qbRenderer_* self, qbRenderGroup group) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  qb_rendergroup_removeuniform_bybinding(group, r->model_uniform);
}

void model_add(qbRenderer self, qbRenderGroup model) {
  qb_renderpass_append(((qbForwardRenderer)self)->scene_3d_pass, model);
}

void model_remove(qbRenderer self, qbRenderGroup model) {
  qb_renderpass_remove(((qbForwardRenderer)self)->scene_3d_pass, model);
}

void update_camera_ubo(qbForwardRenderer r, const struct qbCamera_* camera) {
  CameraUniform m;
  m.vp = glms_mat4_mul(camera->projection_mat, camera->view_mat);
  qb_gpubuffer_update(r->camera_ubo, 0, sizeof(CameraUniform), &m);
}

void update_light_ubo(qbForwardRenderer r, const struct qbCamera_* camera) {
  LightUniform l = {};
  for (size_t i = 0; i < r->directional_lights.size(); ++i) {
    if (r->enabled_directional_lights[i]) {
      l.directionals[i] = r->directional_lights[i];
    }
  }

  for (size_t i = 0; i < r->point_lights.size(); ++i) {
    if (r->enabled_point_lights[i]) {
      l.points[i] = r->point_lights[i];
    }
  }

  for (size_t i = 0; i < r->spot_lights.size(); ++i) {
    if (r->enabled_spot_lights[i]) {
      l.spots[i] = r->spot_lights[i];
    }
  }
  l.view_pos = camera->origin;

  qb_gpubuffer_update(r->light_ubo, 0, sizeof(LightUniform), &l);
}

void render(struct qbRenderer_* self, const struct qbCamera_* camera, qbRenderEvent event) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  qbFrameBuffer camera_fbo = qb_camera_fbo(camera);

  update_camera_ubo(r, camera);
  update_light_ubo(r, camera);
  {
    qbClearValue_ clear = {};
    clear.attachments = qbFrameBufferAttachment(QB_COLOR_ATTACHMENT | QB_DEPTH_ATTACHMENT);
    clear.color = vec4s{ 0.0f, 0.0f, 0.0f, 1.0f };
    clear.depth = 1.0f;
    qb_framebuffer_clear(camera_fbo, &clear);
  }
  qb_renderpass_draw(r->scene_3d_pass, camera_fbo);

  if (1)
  {
    {
      qbBlurArgs args;
      args.horizontal = 1;
      qb_gpubuffer_update(r->blur_ubo, 0, sizeof(qbBlurArgs), &args);

      qbImage read[] = { qb_framebuffer_target(camera_fbo, 1) };
      qbFrameBuffer write = qb_surface_target(r->blur_surface, 0);
      qb_surface_draw(r->blur_surface, read, write);
    }

    for (int i = 0; i < 10; ++i) {
      qbBlurArgs args;
      args.horizontal = i % 2;
      qb_gpubuffer_update(r->blur_ubo, 0, sizeof(qbBlurArgs), &args);

      qbImage read[] = { qb_framebuffer_target(qb_surface_target(r->blur_surface, i % 2), 0) };
      qbFrameBuffer write = qb_surface_target(r->blur_surface, (i + 1) % 2);
      qb_surface_draw(r->blur_surface, read, write);
    }
  }
  if (1)
  {
    qbFrameBuffer blur_frame = qb_surface_target(r->blur_surface, 0);
    qbImage read[] = {
      qb_framebuffer_target(blur_frame, 0),
      qb_framebuffer_target(camera_fbo, 0)
    };
    qbFrameBuffer write = qb_surface_target(r->merge_surface, 0);

    qbMergeArgs args;
    args.bloom = true;
    args.exposure = 1.0f;
    qb_gpubuffer_update(r->merge_ubo, 0, sizeof(qbMergeArgs), &args);

    qb_surface_draw(r->merge_surface, read, write);
  }

  qbFrameBuffer final = qb_surface_target(r->merge_surface, 0);

  qb_renderpass_draw(r->gui_pass, final);

  qb_renderpipeline_present(self->render_pipeline, final, event);
}

void render_callback(qbFrame* f) {
  qbRenderEvent event = (qbRenderEvent)f->event;
  render(event->renderer, event->camera, event);
}

qbMeshBuffer meshbuffer_create(qbRenderer self, qbMesh mesh) {
  qbGpuBuffer verts;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = qbGpuBufferType::QB_GPU_BUFFER_TYPE_VERTEX;
    attr.data = mesh->vertices;
    attr.elem_size = sizeof(float);
    attr.size = mesh->vertex_count * sizeof(vec3s);
    qb_gpubuffer_create(&verts, &attr);
  }

  qbGpuBuffer normals;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = qbGpuBufferType::QB_GPU_BUFFER_TYPE_VERTEX;
    attr.data = mesh->normals;
    attr.elem_size = sizeof(float);
    attr.size = mesh->normal_count * sizeof(vec3s);
    qb_gpubuffer_create(&normals, &attr);
  }

  qbGpuBuffer uvs;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = qbGpuBufferType::QB_GPU_BUFFER_TYPE_VERTEX;
    attr.data = mesh->uvs;
    attr.elem_size = sizeof(float);
    attr.size = mesh->uv_count * sizeof(vec2s);
    qb_gpubuffer_create(&uvs, &attr);
  }

  qbGpuBuffer indices;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = qbGpuBufferType::QB_GPU_BUFFER_TYPE_INDEX;
    attr.data = mesh->indices;
    attr.elem_size = sizeof(uint32_t);
    attr.size = mesh->index_count * sizeof(uint32_t);
    qb_gpubuffer_create(&indices, &attr);
  }

  qbMeshBufferAttr_ attr = {};
  attr.descriptor = *((qbForwardRenderer)self)->supported_geometry;

  qbMeshBuffer ret;
  qb_meshbuffer_create(&ret, &attr);

  qbGpuBuffer buffers[] = { verts, normals, uvs };
  qb_meshbuffer_attachvertices(ret, buffers);
  qb_meshbuffer_attachindices(ret, indices);
  return ret;
}

void meshbuffer_attach_textures(qbRenderer self, qbMeshBuffer buffer,
                                size_t count,
                                uint32_t texture_units[],
                                qbImage textures[]) {
  uint32_t tex_units[16] = {};

  qbForwardRenderer r = (qbForwardRenderer)self;
  assert(count <= r->texture_units_count && count < 16 && "Unsupported amount of textures");

  for (size_t i = 0; i < count; ++i) {
    uint32_t texture_unit = texture_units[i];
    assert(texture_unit < r->texture_units_count && "Texture unit out of bounds");
    tex_units[i] = texture_unit + r->texture_start_binding;
  }

  qb_meshbuffer_attachimages(buffer, count, tex_units, textures);
}

void meshbuffer_attach_uniforms(qbRenderer self, qbMeshBuffer buffer,
                                size_t count,
                                uint32_t uniform_bindings[],
                                qbGpuBuffer uniforms[]) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  std::vector<uint32_t> bindings;
  bindings.resize(count);

  for (size_t i = 0; i < count; ++i) {
    bindings[i] = uniform_bindings[i] + r->uniform_start_binding;
  }
  qb_meshbuffer_attachuniforms(buffer, count, bindings.data(), uniforms);
}

void rendergroup_attach_material(struct qbRenderer_* self, qbRenderGroup group,
                                 struct qbMaterial_* material) {
  qbForwardRenderer r = (qbForwardRenderer)self;

  uint32_t units[6] = {};
  qbImage images[6] = {};

  uint32_t count = 0;
  uint32_t material_tex_units[] = {
    r->albedo_map_binding,
    r->normal_map_binding,
    r->metallic_map_binding,
    r->roughness_map_binding,
    r->ao_map_binding,
    r->emission_map_binding,
  };

  qbImage material_images[] = {
    material->albedo_map ? material->albedo_map : r->empty_albedo_map,
    material->normal_map ? material->normal_map : r->empty_normal_map,
    material->metallic_map ? material->metallic_map : r->empty_metallic_map,
    material->roughness_map ? material->roughness_map : r->empty_roughness_map,
    material->ao_map ? material->ao_map : r->empty_ao_map,
    material->emission_map ? material->emission_map : r->empty_emission_map,
  };

  for (int i = 0; i < 6; ++i) {
    qbImage image = material_images[i];
    if (image) {
      units[count] = material_tex_units[i];
      images[count] = image;
      ++count;
    }
  }

  qb_rendergroup_attachimages(group, count, units, images);
}

void rendergroup_attach_textures(struct qbRenderer_* self, qbRenderGroup group,
                                 size_t count,
                                 uint32_t texture_units[],
                                 qbImage textures[]) {
  uint32_t tex_units[16] = {};

  qbForwardRenderer r = (qbForwardRenderer)self;
  assert(count <= r->texture_units_count && count < 16 && "Unsupported amount of textures");

  for (size_t i = 0; i < count; ++i) {
    uint32_t texture_unit = texture_units[i];
    assert(texture_unit < r->texture_units_count && "Texture unit out of bounds");
    tex_units[i] = texture_unit + r->texture_start_binding;
  }

  qb_rendergroup_attachimages(group, count, tex_units, textures);
}

void rendergroup_attach_uniforms(struct qbRenderer_* self, qbRenderGroup group,
                                 size_t count,
                                 uint32_t uniform_bindings[],
                                 qbGpuBuffer uniforms[]) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  std::vector<uint32_t> bindings;
  bindings.resize(count);

  for (size_t i = 0; i < count; ++i) {
    bindings[i] = uniform_bindings[i] + r->uniform_start_binding;
  }
  qb_rendergroup_attachuniforms(group, count, bindings.data(), uniforms);
}

void light_enable(struct qbRenderer_* self, qbId id, qbLightType light_type) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  switch (light_type) {
    case qbLightType::QB_LIGHT_TYPE_DIRECTIONAL:
      if (id < kMaxDirectionalLights) {
        r->enabled_directional_lights[id] = true;;
      }
      break;
    case qbLightType::QB_LIGHT_TYPE_POINT:
      if (id < kMaxPointLights) {
        r->enabled_point_lights[id] = true;
      }
      break;
    case qbLightType::QB_LIGHT_TYPE_SPOTLIGHT:
      if (id < kMaxSpotLights) {
        r->enabled_spot_lights[id] = true;
      }
      break;
  }
}

void light_disable(struct qbRenderer_* self, qbId id, qbLightType light_type) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  switch (light_type) {
    case qbLightType::QB_LIGHT_TYPE_DIRECTIONAL:
      if (id < kMaxDirectionalLights) {
        r->enabled_directional_lights[id] = false;
      }
      break;
    case qbLightType::QB_LIGHT_TYPE_POINT:
      if (id < kMaxPointLights) {
        r->enabled_point_lights[id] = false;
      }
      break;
    case qbLightType::QB_LIGHT_TYPE_SPOTLIGHT:
      if (id < kMaxSpotLights) {
        r->enabled_spot_lights[id] = false;
      }
      break;
  }
}

bool light_isenabled(struct qbRenderer_* self, qbId id, qbLightType light_type) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  switch (light_type) {
    case qbLightType::QB_LIGHT_TYPE_DIRECTIONAL:
      if (id < kMaxDirectionalLights) {
        return r->enabled_directional_lights[id];
      }
      break;
    case qbLightType::QB_LIGHT_TYPE_POINT:
      if (id < kMaxPointLights) {
        return r->enabled_point_lights[id];
      }
      break;
    case qbLightType::QB_LIGHT_TYPE_SPOTLIGHT:
      if (id < kMaxSpotLights) {
        return r->enabled_spot_lights[id];
      }
      break;
  }
  return false;
}

void light_directional(struct qbRenderer_* self, qbId id, vec3s rgb, vec3s dir, float brightness) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  if (id >= kMaxDirectionalLights) {
    return;
  }

  r->directional_lights[id] = LightDirectional{ rgb, brightness, dir };
}

void light_point(struct qbRenderer_* self, qbId id, vec3s rgb, vec3s pos, float brightness, float range) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  if (id >= kMaxPointLights) {
    return;
  }

  r->point_lights[id] = LightPoint{ rgb, brightness, pos, range };
}

void light_spot(struct qbRenderer_* self, qbId id, vec3s rgb, vec3s pos, vec3s dir,
                float brightness, float range, float angle_deg) {
  qbForwardRenderer r = (qbForwardRenderer)self;
  if (id >= kMaxSpotLights) {
    return;
  }

  r->spot_lights[id] = LightSpot{ rgb, brightness, pos, range, dir, angle_deg };
}

size_t light_max(struct qbRenderer_* self, qbLightType light_type) {
  qbForwardRenderer r = (qbForwardRenderer)self;

  switch (light_type) {
    case qbLightType::QB_LIGHT_TYPE_DIRECTIONAL:
      return kMaxDirectionalLights;
    case qbLightType::QB_LIGHT_TYPE_POINT:
      return kMaxPointLights;
    case qbLightType::QB_LIGHT_TYPE_SPOTLIGHT:
      return kMaxSpotLights;
  }

  return 0;
}

qbFrameBuffer camera_framebuffer_create(struct qbRenderer_* self, uint32_t width, uint32_t height) {
  qbFrameBufferAttr_ fbo_attr = {};
  fbo_attr.width = width;
  fbo_attr.height = height;

  qbFrameBufferAttachment attachments[] = {
    (qbFrameBufferAttachment)(QB_COLOR_ATTACHMENT | QB_DEPTH_ATTACHMENT),
    QB_COLOR_ATTACHMENT,
  };

  uint32_t bindings[] = { 0, 1 };

  fbo_attr.attachments = attachments;
  fbo_attr.color_binding = bindings;
  fbo_attr.attachments_count = sizeof(attachments) / sizeof(qbFrameBufferAttachment);

  qbFrameBuffer fbo;
  qb_framebuffer_create(&fbo, &fbo_attr);

  return fbo;
}

void init_supported_geometry(qbForwardRenderer forward_renderer) {
  // https://stackoverflow.com/questions/40450342/what-is-the-purpose-of-binding-from-vkvertexinputbindingdescription
  qbBufferBinding attribute_bindings = new qbBufferBinding_[3];
  {
    qbBufferBinding binding = attribute_bindings;
    binding->binding = 0;
    binding->stride = 3 * sizeof(float); // Vertex(x, y, z)
    binding->input_rate = QB_VERTEX_INPUT_RATE_VERTEX;
  }
  {
    qbBufferBinding binding = attribute_bindings + 1;
    binding->binding = 1;
    binding->stride = 3 * sizeof(float); // Normal(x, y, z)
    binding->input_rate = QB_VERTEX_INPUT_RATE_VERTEX;
  }
  {
    qbBufferBinding binding = attribute_bindings + 2;
    binding->binding = 2;
    binding->stride = 2 * sizeof(float); // Texture(u, v)
    binding->input_rate = QB_VERTEX_INPUT_RATE_VERTEX;
  }
  /*{
    qbBufferBinding binding = attribute_bindings + 1;
    binding->binding = 3;
    binding->stride = 2 * sizeof(float);
    binding->input_rate = QB_VERTEX_INPUT_RATE_INSTANCE;
  }*/

  qbVertexAttribute attributes = new qbVertexAttribute_[4];
  {
    qbVertexAttribute_* attr = attributes;
    *attr = {};
    attr->binding = 0;
    attr->location = 0;

    attr->count = 3;
    attr->type = QB_VERTEX_ATTRIB_TYPE_FLOAT;
    attr->name = QB_VERTEX_ATTRIB_NAME_VERTEX;
    attr->normalized = false;
    attr->offset = (void*)0;
  }
  {
    qbVertexAttribute_* attr = attributes + 1;
    *attr = {};
    attr->binding = 1;
    attr->location = 1;

    attr->count = 3;
    attr->type = QB_VERTEX_ATTRIB_TYPE_FLOAT;
    attr->name = QB_VERTEX_ATTRIB_NAME_NORMAL;
    attr->normalized = false;
    attr->offset = (void*)(0 * sizeof(float));
  }
  {
    qbVertexAttribute_* attr = attributes + 2;
    *attr = {};
    attr->binding = 2;
    attr->location = 2;

    attr->count = 2;
    attr->type = QB_VERTEX_ATTRIB_TYPE_FLOAT;
    attr->name = QB_VERTEX_ATTRIB_NAME_TEXTURE;
    attr->normalized = false;
    attr->offset = (void*)(0 * sizeof(float));
  }
  /*{
    qbVertexAttribute_* attr = attributes + 3;
    *attr = {};
    attr->binding = 3;
    attr->location = 3;

    attr->count = 2;
    attr->type = QB_VERTEX_ATTRIB_TYPE_FLOAT;
    attr->name = QB_VERTEX_ATTRIB_NAME_PARAM;
    attr->normalized = false;
    attr->offset = (void*)0;
  }*/

  forward_renderer->supported_geometry = new qbGeometryDescriptor_;
  qbGeometryDescriptor supported_geometry = forward_renderer->supported_geometry;
  supported_geometry->attributes = attributes;
  supported_geometry->attributes_count = 3;
  supported_geometry->bindings = attribute_bindings;
  supported_geometry->bindings_count = 3;
}

qbSurface create_blur_surface(qbForwardRenderer r, uint32_t width, uint32_t height) {
  qbSurfaceAttr_ attr;
  attr.width = width;
  attr.height = height;
  attr.vs = "resources/blur.vs";
  attr.fs = "resources/blur.fs";

  qbShaderResourceInfo_ resources[2] = {};
  {
    qbShaderResourceInfo info = resources;
    info->binding = 0;
    info->resource_type = QB_SHADER_RESOURCE_TYPE_UNIFORM_BUFFER;
    info->stages = QB_SHADER_STAGE_FRAGMENT;
    info->name = "qb_blur_args";
  }
  {
    qbShaderResourceInfo info = resources + 1;
    info->binding = 1;
    info->resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
    info->stages = QB_SHADER_STAGE_FRAGMENT;
    info->name = "qb_blur_sampler";
  }
  attr.resources = resources;
  attr.resources_count = sizeof(resources) / sizeof(resources[0]);

  qbImageSampler sampler;
  {
    qbImageSamplerAttr_ attr = {};
    attr.image_type = QB_IMAGE_TYPE_2D;
    attr.min_filter = QB_FILTER_TYPE_LINEAR;
    attr.mag_filter = QB_FILTER_TYPE_LINEAR;
    attr.s_wrap = qbImageWrapType::QB_IMAGE_WRAP_TYPE_CLAMP_TO_EDGE;
    attr.t_wrap = qbImageWrapType::QB_IMAGE_WRAP_TYPE_CLAMP_TO_EDGE;
    qb_imagesampler_create(&sampler, &attr);
  }
  uint32_t sampler_bindings[] = { 1 };
  qbImageSampler samplers[] = { sampler };
  attr.samplers = samplers;
  attr.sampler_bindings = sampler_bindings;
  attr.samplers_count = 1;

  qbGpuBuffer ubo;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = QB_GPU_BUFFER_TYPE_UNIFORM;
    attr.data = nullptr;
    attr.size = sizeof(qbBlurArgs);

    qb_gpubuffer_create(&r->blur_ubo, &attr);
  }
  uint32_t ubo_bindings[] = { 0 };
  qbGpuBuffer uniforms[] = { r->blur_ubo };
  attr.uniforms = uniforms;
  attr.uniform_bindings = ubo_bindings;
  attr.uniforms_count = 1;

  attr.pixel_format = QB_PIXEL_FORMAT_RGBA8;
  attr.target_count = 2;

  qbSurface ret;
  qb_surface_create(&ret, &attr);
  return ret;
}

float cube_sdf(vec3s p, vec3s c, float size) {
  // If d.x < 0, then -1 < p.x < 1, and same logic applies to p.y, p.z
  // So if all components of d are negative, then p is inside the unit cube
  vec3s d = glms_vec3_sub(glms_vec3_abs(glms_vec3_sub(p, c)), vec3s{ size * 0.5f, size * 0.5f, size * 0.5f });

  // Assuming p is inside the cube, how far is it from the surface?
  // Result will be negative or zero.
  float insideDistance = glm_min(glm_max(d.x, glm_max(d.y, d.z)), 0.0);

  // Assuming p is outside the cube, how far is it from the surface?
  // Result will be positive or zero.
  d.x = glm_max(d.x, 0.0f);
  d.y = glm_max(d.y, 0.0f);
  d.z = glm_max(d.z, 0.0f);
  float outsideDistance = glms_vec3_norm(d);

  return insideDistance + outsideDistance;
}

float sphere_sdf(vec3s p, vec3s c, float radius) {
  return glms_vec3_norm(glms_vec3_sub(p, c)) - radius;
}

qbSurface create_merge_surface(qbForwardRenderer r, uint32_t width, uint32_t height) {
  qbSurfaceAttr_ attr;
  attr.width = width;
  attr.height = height;
  attr.vs = "resources/merge.vs";
  attr.fs = "resources/merge.fs";

  qbShaderResourceInfo_ resources[3] = {};
  {
    qbShaderResourceInfo info = resources;
    info->binding = 0;
    info->resource_type = QB_SHADER_RESOURCE_TYPE_UNIFORM_BUFFER;
    info->stages = QB_SHADER_STAGE_FRAGMENT;
    info->name = "qb_merge_args";
  }
  {
    qbShaderResourceInfo info = resources + 1;
    info->binding = 1;
    info->resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
    info->stages = QB_SHADER_STAGE_FRAGMENT;
    info->name = "qb_merge_sampler";
  }
  {
    qbShaderResourceInfo info = resources + 2;
    info->binding = 2;
    info->resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
    info->stages = QB_SHADER_STAGE_FRAGMENT;
    info->name = "qb_scene_sampler";
  }
  attr.resources = resources;
  attr.resources_count = sizeof(resources) / sizeof(resources[0]);

  qbImageSampler samplers[2];
  {
    qbImageSamplerAttr_ attr = {};
    attr.image_type = QB_IMAGE_TYPE_2D;
    attr.min_filter = QB_FILTER_TYPE_LINEAR;
    attr.mag_filter = QB_FILTER_TYPE_LINEAR;
    qb_imagesampler_create(samplers, &attr);
  }
  {
    qbImageSamplerAttr_ attr = {};
    attr.image_type = QB_IMAGE_TYPE_2D;
    attr.min_filter = QB_FILTER_TYPE_LINEAR;
    attr.mag_filter = QB_FILTER_TYPE_LINEAR;
    qb_imagesampler_create(samplers + 1, &attr);
  }
  uint32_t sampler_bindings[] = { 1, 2 };
  attr.samplers = samplers;
  attr.sampler_bindings = sampler_bindings;
  attr.samplers_count = 2;

  qbGpuBuffer ubo;
  {
    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = QB_GPU_BUFFER_TYPE_UNIFORM;
    attr.data = nullptr;
    attr.size = sizeof(qbMergeArgs);

    qb_gpubuffer_create(&r->merge_ubo, &attr);
  }
  uint32_t ubo_bindings[] = { 0 };
  qbGpuBuffer uniforms[] = { r->merge_ubo };
  attr.uniforms = uniforms;
  attr.uniform_bindings = ubo_bindings;
  attr.uniforms_count = 1;

  attr.pixel_format = QB_PIXEL_FORMAT_RGBA8;
  attr.target_count = 1;

  qbSurface ret;
  qb_surface_create(&ret, &attr);
  return ret;
}

struct qbRenderer_* qb_forwardrenderer_create(uint32_t width, uint32_t height, struct qbRendererAttr_* args) {
  qbForwardRenderer ret = new qbForwardRenderer_;

  ret->renderer.width = width;
  ret->renderer.height = height;
  ret->renderer.rendergroup_oncreate = rendergroup_oncreate;
  ret->renderer.rendergroup_ondestroy = rendergroup_ondestroy;
  ret->renderer.rendergroup_add = model_add;
  ret->renderer.rendergroup_remove = model_remove;
  ret->renderer.rendergroup_attach_textures = rendergroup_attach_textures;
  ret->renderer.rendergroup_attach_uniforms = rendergroup_attach_uniforms;
  ret->renderer.meshbuffer_create = meshbuffer_create;
  ret->renderer.meshbuffer_attach_textures = meshbuffer_attach_textures;
  ret->renderer.rendergroup_attach_material = rendergroup_attach_material;
  ret->gui_pass = args->opt_gui_renderpass;

  ret->renderer.light_enable = light_enable;
  ret->renderer.light_disable = light_disable;
  ret->renderer.light_isenabled = light_isenabled;
  ret->renderer.light_directional = light_directional;
  ret->renderer.light_point = light_point;
  ret->renderer.light_spot = light_spot;
  ret->renderer.light_max = light_max;
  ret->renderer.camera_framebuffer_create = camera_framebuffer_create;

  ret->renderer.render = render;
  ret->directional_lights = {};
  ret->enabled_directional_lights = {};
  ret->point_lights = {};
  ret->enabled_point_lights = {};
  ret->spot_lights = {};
  ret->enabled_spot_lights = {};

  {
    // If there is a sampler defined, there needs to be a corresponding image,
    // otherwise there is undetermined behavior. In the case that there isn't a
    // image given, an empty image is placed in its stead.
    char pixel_0[4] = {};
    qbPixelMap map_0 = qb_pixelmap_create(1, 1, 0, qbPixelFormat::QB_PIXEL_FORMAT_RGBA8, pixel_0);

    char pixel_1[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
    qbPixelMap map_1 = qb_pixelmap_create(1, 1, 0, qbPixelFormat::QB_PIXEL_FORMAT_RGBA8, pixel_1);

    char pixel_n[4] = { 0x80, 0x80, 0xFF, 0xFF };
    qbPixelMap map_n = qb_pixelmap_create(1, 1, 0, qbPixelFormat::QB_PIXEL_FORMAT_RGBA8, pixel_n);

    qbImageAttr_ attr = {};
    attr.type = qbImageType::QB_IMAGE_TYPE_2D;

    qb_image_create(&ret->empty_albedo_map, &attr, map_1);
    qb_image_create(&ret->empty_normal_map, &attr, map_n);
    qb_image_create(&ret->empty_metallic_map, &attr, map_0);
    qb_image_create(&ret->empty_roughness_map, &attr, map_0);
    qb_image_create(&ret->empty_ao_map, &attr, map_0);
    qb_image_create(&ret->empty_emission_map, &attr, map_0);
  }
  {
    qbRenderPipelineAttr_ attr = {};
    attr.viewport = { 0.0f, 0.0f, (float)width, (float)height };
    attr.viewport_scale = 1.0f;
    attr.name = "qbForwardRenderer";

    qb_renderpipeline_create(&ret->renderer.render_pipeline, &attr);
  }

  init_supported_geometry(ret);

  qbShaderModule shader_module;

  std::vector<qbShaderResourceInfo_> resources;
  std::vector<uint32_t> uniform_bindings;
  std::vector<std::pair<qbShaderResourceInfo_, qbGpuBuffer>> resource_uniforms;
  std::vector<uint32_t> sampler_bindings;
  std::vector<std::pair<qbShaderResourceInfo_, qbImageSampler>> resource_samplers;

  size_t native_uniform_count;
  size_t native_sampler_count;

  size_t uniform_start_binding;
  size_t user_uniform_count = args->uniform_count;

  size_t sampler_start_binding;
  size_t user_sampler_count = args->image_sampler_count;

  ret->camera_uniform_name = "qb_camera";
  ret->model_uniform_name = "qb_model";
  ret->material_uniform_name = "qb_material";
  ret->light_uniform_name = "qb_lights";

  {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_UNIFORM_BUFFER;
    info.stages = QB_SHADER_STAGE_VERTEX;
    info.name = ret->camera_uniform_name.data();

    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = QB_GPU_BUFFER_TYPE_UNIFORM;
    attr.data = nullptr;
    attr.size = sizeof(CameraUniform);

    qb_gpubuffer_create(&ret->camera_ubo, &attr);
    resource_uniforms.push_back({ info, ret->camera_ubo });
  }
  {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_UNIFORM_BUFFER;
    info.stages = QB_SHADER_STAGE_VERTEX;
    info.name = ret->model_uniform_name.data();

    resource_uniforms.push_back({ info, nullptr });
  }
  {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_UNIFORM_BUFFER;
    info.stages = QB_SHADER_STAGE_FRAGMENT;
    info.name = ret->material_uniform_name.data();

    resource_uniforms.push_back({ info, nullptr });
  }
  {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_UNIFORM_BUFFER;
    info.stages = QB_SHADER_STAGE_FRAGMENT;
    info.name = ret->light_uniform_name.data();

    qbGpuBufferAttr_ attr = {};
    attr.buffer_type = QB_GPU_BUFFER_TYPE_UNIFORM;
    attr.data = nullptr;
    attr.size = sizeof(LightUniform);

    qb_gpubuffer_create(&ret->light_ubo, &attr);
    resource_uniforms.push_back({ info, ret->light_ubo });
  }
  {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
    info.stages = QB_SHADER_STAGE_FRAGMENT;
    info.name = "qb_texture_unit_1";

    qbImageSamplerAttr_ attr = {};
    attr.image_type = QB_IMAGE_TYPE_2D;
    attr.min_filter = QB_FILTER_TYPE_LIENAR_MIPMAP_LINEAR;
    attr.mag_filter = QB_FILTER_TYPE_LINEAR;

    qbImageSampler sampler;
    qb_imagesampler_create(&sampler, &attr);

    //resource_samplers.push_back({ info, sampler });
  }
  {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
    info.stages = QB_SHADER_STAGE_FRAGMENT;
    info.name = "qb_texture_unit_2";

    qbImageSamplerAttr_ attr = {};
    attr.image_type = QB_IMAGE_TYPE_2D;
    attr.min_filter = QB_FILTER_TYPE_LIENAR_MIPMAP_LINEAR;
    attr.mag_filter = QB_FILTER_TYPE_LINEAR;

    qbImageSampler sampler;
    qb_imagesampler_create(&sampler, &attr);

    //resource_samplers.push_back({ info, sampler });
  }

  std::vector<std::string> material_sampler_names = {
    "qb_material_albedo_map",
    "qb_material_normal_map",
    "qb_material_metallic_map",
    "qb_material_roughness_map",
    "qb_material_ao_map",
    "qb_material_emission_map",
  };
  for (auto& sampler_name : material_sampler_names) {
    qbShaderResourceInfo_ info = {};
    info.resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
    info.stages = QB_SHADER_STAGE_FRAGMENT;
    info.name = sampler_name.data();

    qbImageSamplerAttr_ attr = {};
    attr.image_type = QB_IMAGE_TYPE_2D;
    attr.min_filter = QB_FILTER_TYPE_LIENAR_MIPMAP_LINEAR;
    attr.mag_filter = QB_FILTER_TYPE_LINEAR;

    qbImageSampler sampler;
    qb_imagesampler_create(&sampler, &attr);

    resource_samplers.push_back({ info, sampler });
  }

  native_uniform_count = resource_uniforms.size();
  native_sampler_count = resource_samplers.size();
  {
    std::vector<std::pair<qbShaderResourceInfo_, qbGpuBuffer>> user_uniforms;
    user_uniforms.resize(args->shader_resource_count);
    for (size_t i = 0; i < args->shader_resource_count; ++i) {
      qbShaderResourceInfo info = args->shader_resources + i;
      uint32_t binding = info->binding;
      user_uniforms[binding].first = *info;
    }
    for (size_t i = 0; i < args->uniform_count; ++i) {
      qbGpuBuffer uniform = args->uniforms[i];
      uint32_t binding = args->uniform_bindings[i];
      user_uniforms[binding].second = uniform;
    }
    for (auto&& e : user_uniforms) {
      resource_uniforms.push_back(e);
    }

    std::vector<std::pair<qbShaderResourceInfo_, qbImageSampler>> user_samplers;
    user_samplers.resize(args->image_sampler_count);
    for (size_t i = 0; i < args->image_sampler_count; ++i) {
      qbImageSampler sampler = args->image_samplers[i];

      qbShaderResourceInfo_ info;
      info.resource_type = QB_SHADER_RESOURCE_TYPE_IMAGE_SAMPLER;
      info.stages = QB_SHADER_STAGE_FRAGMENT;
      info.name = qb_imagesampler_name(sampler);

      user_samplers.push_back({ info, sampler });
    }
    for (auto&& e : user_samplers) {
      resource_samplers.push_back(e);
    }
  }

  uniform_bindings.reserve(resource_uniforms.size());
  sampler_bindings.reserve(resource_samplers.size());
  std::vector<qbGpuBuffer> uniforms;
  std::vector<qbImageSampler> samplers;
  {
    uint32_t binding = 0;

    size_t uniform_index = 0;
    for (auto& e : resource_uniforms) {
      e.first.binding = binding++;
      resources.push_back(e.first);

      if (e.second) {
        uniforms.push_back(e.second);
        uniform_bindings.push_back(e.first.binding);
      }

      if (std::string(e.first.name) == ret->camera_uniform_name) {
        ret->camera_uniform = e.first.binding;
      } else if (std::string(e.first.name) == ret->model_uniform_name) {
        ret->model_uniform = e.first.binding;
      } else if (std::string(e.first.name) == ret->material_uniform_name) {
        ret->material_uniform = e.first.binding;
      } else if (std::string(e.first.name) == ret->light_uniform_name) {
        ret->light_uniform = e.first.binding;
      }

      if (uniform_index == native_uniform_count) {
        uniform_start_binding = binding;
      }
      ++uniform_index;
    }

    size_t sampler_index = 0;
    for (auto& e : resource_samplers) {
      e.first.binding = binding;
      resources.push_back(e.first);
      samplers.push_back(e.second);
      sampler_bindings.push_back(binding++);

      if (std::string(e.first.name) == "qb_material_albedo_map") {
        ret->albedo_map_binding = e.first.binding;
      } else if (std::string(e.first.name) == "qb_material_normal_map") {
        ret->normal_map_binding = e.first.binding;
      } else if (std::string(e.first.name) == "qb_material_metallic_map") {
        ret->metallic_map_binding = e.first.binding;
      } else if (std::string(e.first.name) == "qb_material_roughness_map") {
        ret->roughness_map_binding = e.first.binding;
      } else if (std::string(e.first.name) == "qb_material_ao_map") {
        ret->ao_map_binding = e.first.binding;
      } else if (std::string(e.first.name) == "qb_material_emission_map") {
        ret->emission_map_binding = e.first.binding;
      }

      if (sampler_index == native_sampler_count) {
        sampler_start_binding = binding;
      }
      ++sampler_index;
    }
  }

  {
    qbShaderModuleAttr_ attr = {};
    attr.vs = "resources/pbr.vs";
    attr.fs = "resources/pbr.fs";
    attr.resources = resources.data();
    attr.resources_count = resources.size();

    qb_shadermodule_create(&shader_module, &attr);
    qb_shadermodule_attachuniforms(shader_module,
                                   uniform_bindings.size(),
                                   uniform_bindings.data(),
                                   uniforms.data());

    qb_shadermodule_attachsamplers(shader_module,
                                   sampler_bindings.size(),
                                   sampler_bindings.data(),
                                   samplers.data());
  }

  {
    qbClearValue_ clear = {};
    clear.attachments = (qbFrameBufferAttachment)(qbFrameBufferAttachment::QB_COLOR_ATTACHMENT |
                                                  qbFrameBufferAttachment::QB_DEPTH_ATTACHMENT);
    clear.depth = 0.0;
    clear.color = { 1.0, 1.0, 0.0, 1.0 };

    qbRenderPassAttr_ attr = {};
    attr.viewport = { 0.0, 0.0, (float)width, (float)height };
    attr.supported_geometry = *ret->supported_geometry;
    attr.shader = shader_module;
    attr.viewport_scale = 1.0f;
    attr.clear = clear;

    qb_renderpass_create(&ret->scene_3d_pass, &attr);
  }

  {
    qbSystemAttr attr;
    qb_systemattr_create(&attr);
    qb_systemattr_setcallback(attr, render_callback);
    qb_systemattr_addconst(attr, qb_renderable());
    qb_systemattr_addconst(attr, qb_material());
    qb_systemattr_addmutable(attr, qb_transform());
    qb_systemattr_setjoin(attr, qbComponentJoin::QB_JOIN_LEFT);
    qb_systemattr_setfunction(attr, [](qbInstance* insts, qbFrame* f) {
      qbRenderEvent event = (qbRenderEvent)f->event;
      qbForwardRenderer renderer = (qbForwardRenderer)event->renderer;

      qbRenderable* renderable;
      qbMaterial* material;
      qbTransform transform;
      qb_instance_getconst(insts[0], &renderable);
      qb_instance_getconst(insts[1], &material);
      qb_instance_getmutable(insts[2], &transform);

      qb_renderable_upload(*renderable, *material);
      qbRenderGroup group = qb_renderable_rendergroup(*renderable);
      qbGpuBuffer model_buffer = qb_rendergroup_finduniform_bybinding(group, renderer->model_uniform);
      qbGpuBuffer material_buffer = qb_rendergroup_finduniform_bybinding(group, renderer->material_uniform);

      ModelUniform model_uniform;
      mat4s orientation = glms_translate(transform->orientation, transform->pivot);
      mat4s pos = glms_translate(GLMS_MAT4_IDENTITY_INIT, transform->position);
      model_uniform.m = glms_mat4_mul(pos, orientation);
      model_uniform.rot = transform->orientation;
      qb_gpubuffer_update(model_buffer, 0, sizeof(ModelUniform), &model_uniform);

      MaterialUniform material_uniform;
      material_uniform.metallic = (*material)->metallic;
      material_uniform.roughness = (*material)->roughness;
      material_uniform.albedo = (*material)->albedo;
      material_uniform.emission = (*material)->emission;

      qb_gpubuffer_update(material_buffer, 0, sizeof(MaterialUniform), &material_uniform);
    });

    qb_systemattr_settrigger(attr, qbTrigger::QB_TRIGGER_EVENT);
    qb_system_create(&ret->render_system, attr);
    qb_systemattr_destroy(&attr);

    qb_event_subscribe(qb_render_event(), ret->render_system);
  }

  ret->uniform_start_binding = uniform_start_binding;
  ret->uniform_count = user_uniform_count;
  ret->texture_start_binding = sampler_start_binding;
  ret->texture_units_count = user_sampler_count;
  ret->blur_surface = create_blur_surface(ret, width, height);
  ret->merge_surface = create_merge_surface(ret, width, height);
  ret->renderer.state = ret;
  return (qbRenderer)ret;
}

void qb_forwardrenderer_destroy(qbRenderer renderer) {
  qbForwardRenderer r = (qbForwardRenderer)renderer;
  qb_event_unsubscribe(qb_render_event(), r->render_system);
  qb_system_destroy(&r->render_system);
}


text:
package com.sirius.upns.protocol.transfer;

import com.sirius.upns.protocol.business.BusinessPacket;

/**
 * @project protocol
 * @date 2013-9-9-下午2:39:24
 * @author pippo
 */
public interface PayloadMarshaller {

	/**
	 * 反序列化
	 *
	 * @param content byte数组表示的内容
	 * @param payloadClass payload类型
	 * @return Packet
	 * @throws Exception
	 */
	<T extends BusinessPacket> T unmarshal(byte[] content, Class<T> payloadClass) throws Exception;

	/**
	 * 序列化
	 *
	 * @param payload payload
	 * @return byte
	 */
	byte[] marshal(BusinessPacket payload) throws Exception;

}