import numpy as np


def svm_predict(model, X):
    """
    svm_predict returns a vector of predictions using a trained SVM model
    (svmTrain).
        pred = svm_predict(model, X) returns a vector of predictions using a
        trained SVM model (svmTrain). X is a mxn matrix where there each
        example is a row. model is a svm model returned from svmTrain.
        predictions pred is a m x 1 column of predictions of {0, 1} values.
    """

    # check if we are getting a vector. If so, then assume we only need to do predictions
    # for a single example
    if X.shape[1] == 1:
        X = X.T

    m = X.shape[0]
    p = np.zeros(m)
    pred = np.zeros(m)

    if model['kernelFunction'].__name__ == 'linear_kernel':
        # we can use the weights and bias directly if working with the linear kernel
        p = np.dot(X, model['w']) + model['b']
    elif model['kernelFunction'].__name__ == 'gaussian_kernel':
        # vectorized RBF Kernel
        # This is equivalent to computing the kernel on every pair of examples
        X1 = np.sum(X ** 2, 1)
        X2 = np.sum(model['X'] ** 2, 1)
        K = X2 + X1[:, None] - 2 * np.dot(X, model['X'].T)

        if len(model['args']) > 0:
            K /= 2 * model['args'][0] ** 2

        K = np.exp(-K)
        p = np.dot(K, model['alphas'] * model['y']) + model['b']
    else:
        # other non-linear kernel
        for i in range(m):
            predictions = 0
            for j in range(model['X'].shape[0]):
                predictions += model['alphas'][j] * model['y'][j] \
                               * model['kernelFunction'](X[i, :], model['X'][j, :])
            p[i] = predictions

    pred[p >= 0] = 1
    return pred
