class_docstr: Efficient and numerically stable implementation of a dot product followed
  by a modulo operation. This performs the same function as matVecModM.
  
  We do this 2 times on 2 triple inputs and concatenating the output.
code: def c_code(self, node, name, inputs, outputs, sub):
      (_A, _s, _m, _A2, _s2, _m2) = inputs
      (_z,) = outputs
      return """
          int osize = -1;
          if (PyArray_NDIM(%(_A)s) != 2) {PyErr_SetString(PyExc_NotImplementedError, "rank(A) != 2"); %(fail)s;}
          if (PyArray_NDIM(%(_s)s) != 1) {PyErr_SetString(PyExc_NotImplementedError, "rank(v) != 1"); %(fail)s;}
          if (PyArray_NDIM(%(_m)s) != 0) {PyErr_SetString(PyExc_NotImplementedError, "rank(m) != 0"); %(fail)s;}
          if (PyArray_NDIM(%(_A2)s) != 2) {PyErr_SetString(PyExc_NotImplementedError, "rank(A2) != 2"); %(fail)s;}
          if (PyArray_NDIM(%(_s2)s) != 1) {PyErr_SetString(PyExc_NotImplementedError, "rank(v2) != 1"); %(fail)s;}
          if (PyArray_NDIM(%(_m2)s) != 0) {PyErr_SetString(PyExc_NotImplementedError, "rank(m2) != 0"); %(fail)s;}
  
          if( PyArray_DIMS(%(_A)s)[1] != PyArray_DIMS(%(_s)s)[0])
          {PyErr_SetString(PyExc_NotImplementedError, "A and s shapes don't agree."); %(fail)s;}
          if( PyArray_DIMS(%(_A2)s)[1] != PyArray_DIMS(%(_s2)s)[0])
          {PyErr_SetString(PyExc_NotImplementedError, "A2 and s2 shapes don't agree."); %(fail)s;}
  
          osize = PyArray_DIMS(%(_A)s)[0] + PyArray_DIMS(%(_A2)s)[0];
          if (!%(_z)s
              || (PyArray_DIMS(%(_z)s)[0] != osize))
          {
              {Py_XDECREF(%(_z)s);}
              npy_intp dims[] = {0,};
              dims[0] = osize;
              %(_z)s = (PyArrayObject*) PyArray_SimpleNew(1, dims, PyArray_TYPE(%(_s)s));
          }
  
          if(!%(_z)s){%(fail)s;}
  
          {   //makes it compile even though labels jump over variable definitions.
  
              // A has size MxN, s has N, output M
              npy_intp M = PyArray_DIMS(%(_A)s)[0];
              npy_intp N = PyArray_DIMS(%(_A)s)[1];
  
              const dtype_%(_A)s* __restrict__ DA = (dtype_%(_A)s*)PyArray_DATA(%(_A)s);
              dtype_%(_s)s* __restrict__ Ds = (dtype_%(_s)s*)PyArray_DATA(%(_s)s);
              dtype_%(_z)s* __restrict__ Dz = (dtype_%(_z)s*)PyArray_DATA(%(_z)s);
              const dtype_%(_m)s m = ((dtype_%(_m)s*)PyArray_DATA(%(_m)s))[0];
  
              npy_intp SA = PyArray_STRIDES(%(_A)s)[1] / PyArray_DESCR(%(_A)s)->elsize;
              npy_intp Ss = PyArray_STRIDES(%(_s)s)[0] / PyArray_DESCR(%(_s)s)->elsize;
              npy_intp Sz = PyArray_STRIDES(%(_z)s)[0] / PyArray_DESCR(%(_z)s)->elsize;
  
              for (npy_int32 i = 0; i < M; ++i)
              {
                  const dtype_%(_A)s* __restrict__ Ak = (dtype_%(_A)s*)(PyArray_BYTES(%(_A)s) + PyArray_STRIDES(%(_A)s)[0] * i);
  
                  npy_int64 r = 0;
  
                  for (npy_int32 j = 0; j < N; ++j)
                  {
                      r += (npy_int64)(Ds[j * Ss] * (npy_int64)(Ak[j * SA])) %% m;
                  }
  
                  Dz[i * Sz] = r %% m;
              }
          }
  
          //redo it with the second triple of inputs
          {
              // A has size MxN, s has N, output M
              npy_intp M = PyArray_DIMS(%(_A2)s)[0];
              npy_intp N = PyArray_DIMS(%(_A2)s)[1];
  
              const dtype_%(_A2)s* __restrict__ DA = (dtype_%(_A2)s*)PyArray_DATA(%(_A2)s);
              dtype_%(_s2)s* __restrict__ Ds = (dtype_%(_s2)s*)PyArray_DATA(%(_s2)s);
              const dtype_%(_m2)s m = ((dtype_%(_m2)s*)PyArray_DATA(%(_m2)s))[0];
  
              npy_intp SA = PyArray_STRIDES(%(_A2)s)[1] / PyArray_DESCR(%(_A2)s)->elsize;
              npy_intp Ss = PyArray_STRIDES(%(_s2)s)[0] / PyArray_DESCR(%(_s2)s)->elsize;
              npy_intp Sz = PyArray_STRIDES(%(_z)s)[0] / PyArray_DESCR(%(_z)s)->elsize;
  
              dtype_%(_z)s* __restrict__ Dz = (dtype_%(_z)s*)PyArray_DATA(%(_z)s) + PyArray_DIMS(%(_A)s)[0] * Sz;
  
              for (npy_int32 i = 0; i < M; ++i)
              {
                  const dtype_%(_A2)s* __restrict__ Ak = (dtype_%(_A2)s*)(PyArray_BYTES(%(_A2)s) + PyArray_STRIDES(%(_A2)s)[0] * i);
  
                  npy_int64 r = 0;
  
                  for (npy_int32 j = 0; j < N; ++j)
                  {
                      r += (npy_int64)(Ds[j * Ss] * (npy_int64)(Ak[j * SA])) %% m;
                  }
  
                  Dz[i * Sz] = r %% m;
              }
  
          }
  
          """ % dict(
          locals(), **sub
      )
  
imports: from __future__ import absolute_import, print_function, division
  import warnings
  import numpy
  from six import integer_types, string_types
  from six.moves import xrange
  import theano
  from theano import Op, Apply, shared, config, Variable
  from theano import gradient, function
  from theano.gradient import undefined_grad
  from theano import tensor
  from theano.tensor import TensorType, as_tensor_variable, get_vector_length, cast, opt, scal
  from theano.compile import optdb
  from theano.gof import local_optimizer, ParamsType
  from theano.scalar import bool, int32
  from .None import multinomial
label_desc: Use image node_id 4 for calling the DotModulo obj's underlying member method code with example usage: obj.c_code(node, name, inputs, outputs, sub) and returns: unknown