def forward(X, WLSTM, c0=None, h0=None):
        """
        X should be of shape (n,b,input_size), where n = length of sequence, b
        = batch size
        """
        n, b, input_size = X.shape
        d = int(WLSTM.shape[1] / 4)  # hidden size
        if c0 is None:
            c0 = np.zeros((b, d))
        if h0 is None:
            h0 = np.zeros((b, d))

        # Perform the LSTM forward pass with X as the input
        xphpb = WLSTM.shape[0]  # x plus h plus bias, lol
        Hin = np.zeros(
            (n, b, xphpb)
        )  # input [1, xt, ht-1] to each tick of the LSTM
        Hout = np.zeros(
            (n, b, d)
        )  # hidden representation of the LSTM (gated cell content)
        IFOG = np.zeros((n, b, d * 4))  # input, forget, output, gate (IFOG)
        IFOGf = np.zeros((n, b, d * 4))  # after nonlinearity
        C = np.zeros((n, b, d))  # cell content
        Ct = np.zeros((n, b, d))  # tanh of cell content

        for t in range(n):
            # concat [x,h] as input to the LSTM
            prevh = Hout[t - 1] if t > 0 else h0
            Hin[t, :, 0] = 1  # bias
            Hin[t, :, 1 : input_size + 1] = X[t]
            Hin[t, :, input_size + 1 :] = prevh
            # compute all gate activations. dots: (most work is this line)
            IFOG[t] = Hin[t].dot(WLSTM)
            # non-linearities
            IFOGf[t, :, : 3 * d] = 1.0 / (
                1.0 + np.exp(-IFOG[t, :, : 3 * d])
            )  # sigmoids; these are the gates
            IFOGf[t, :, 3 * d :] = np.tanh(IFOG[t, :, 3 * d :])  # tanh
            # compute the cell activation
            prevc = C[t - 1] if t > 0 else c0
            C[t] = (
                IFOGf[t, :, :d] * IFOGf[t, :, 3 * d :]
                + IFOGf[t, :, d : 2 * d] * prevc
            )
            Ct[t] = np.tanh(C[t])
            Hout[t] = IFOGf[t, :, 2 * d : 3 * d] * Ct[t]

        cache = {}
        cache["WLSTM"] = WLSTM
        cache["Hout"] = Hout
        cache["IFOGf"] = IFOGf
        cache["IFOG"] = IFOG
        cache["C"] = C
        cache["Ct"] = Ct
        cache["Hin"] = Hin
        cache["c0"] = c0
        cache["h0"] = h0

        # return C[t], as well so we can continue LSTM with prev state
        # init if needed
        return Hout, C[t], Hout[t], cache

        X should be of shape (n,b,input_size), where n = length of sequence, b
        = batch size
        
tests/todo/lstm_batch.py