Advertisement
Guest User

Untitled

a guest
Apr 26th, 2017
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.59 KB | None | 0 0
  1. PASTEBINnew pastetrends API tools faq
  2. search...
  3.  
  4.  
  5. Guest User
  6. -
  7.      
  8. Public Pastes
  9. LMS
  10. 2 sec ago
  11. Knocks out a secur...
  12. 26 sec ago
  13. Untitled
  14. 29 sec ago
  15. Draw 3 (Owlveeluti...
  16. 31 sec ago
  17. Untitled
  18. 40 sec ago
  19. Untitled
  20. 1 min ago
  21. Untitled
  22. 1 min ago
  23. Untitled
  24. 1 min ago
  25.  
  26. SHARE
  27. TWEET
  28.  
  29.  Lab2
  30.  A GUEST   APR 26TH, 2017   251   IN 29 DAYS
  31.  
  32. Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  33. rawdownloadcloneembedreportprint Python 8.41 KB
  34. from __future__ import division
  35. import scipy.spatial
  36. import numpy as np
  37.  
  38. # MATRIX + ProcessPoolExecutor
  39. def calc_distance(X, X_train):
  40.     X_train = X_train.transpose()
  41.  
  42.     outArr = X.astype(np.uint8) @ X_train.astype(np.uint8)
  43.     outArr += (~X).astype(np.uint8) @ (~X_train).astype(np.uint8)
  44.     return np.subtract(np.uint8(X_train.shape[0]), outArr)
  45.  
  46.  
  47. def hamming_distance(X, X_train):
  48.     """
  49.   :param X: zbior porownwanych obiektow N1xD
  50.   :param X_train: zbior obiektow do ktorych porownujemy N2xD
  51.   Funkcja wyznacza odleglosci Hamminga obiektow ze zbioru X od
  52.   obiektow X_train. ODleglosci obiektow z jednego i drugiego
  53.   zbioru zwrocone zostana w postaci macierzy
  54.   :return: macierz odleglosci pomiedzy obiektami z X i X_train N1xN2
  55.   """
  56.  
  57.     X = X.toarray()
  58.     X_train = X_train.toarray()
  59.  
  60.     #SCIPY
  61.     # return scipy.spatial.distance.cdist(X, X_train, metric="hamming") * X.shape[1]
  62.  
  63.     #MATRIX (uwaga na uint8 overflow)
  64.     X_train = X_train.transpose()
  65.  
  66.     outArr = X.astype(np.uint8) @ X_train.astype(np.uint8)
  67.     outArr += (~X).astype(np.uint8) @ (~X_train).astype(np.uint8)
  68.     return np.subtract(np.uint8(X_train.shape[0]), outArr)
  69.  
  70.     # MATRIX + ProcessPoolExecutor (mocno przyspiesza wykonywanie gdy duże macierze)
  71.     # MAX_PROCESSES = 4
  72.     #
  73.     # futures = []
  74.     # arrays = np.split(X, MAX_PROCESSES)
  75.     #
  76.     # from concurrent.futures import ProcessPoolExecutor, Future
  77.     # with ProcessPoolExecutor(max_workers=MAX_PROCESSES) as executor:
  78.     #     for i in range(MAX_PROCESSES):
  79.     #         futures.append(executor.submit(calc_distance, arrays[i], X_train))
  80.     #
  81.     # futures = list(map(Future.result, futures))
  82.     #
  83.     # return np.vstack(futures)
  84.  
  85. def sort_train_labels_knn(Dist, y):
  86.     """
  87.   Funkcja sortujaca etykiety klas danych treningowych y
  88.   wzgledem prawdopodobienstw zawartych w macierzy Dist.
  89.   Funkcja zwraca macierz o wymiarach N1xN2. W kazdym
  90.   wierszu maja byc posortowane etykiety klas z y wzgledem
  91.   wartosci podobienstw odpowiadajacego wiersza macierzy
  92.   Dist
  93.   :param Dist: macierz odleglosci pomiedzy obiektami z X
  94.   i X_train N1xN2
  95.   :param y: wektor etykiet o dlugosci N2
  96.   :return: macierz etykiet klas posortowana wzgledem
  97.   wartosci podobienstw odpowiadajacego wiersza macierzy
  98.   Dist. Uzyc algorytmu mergesort.
  99.   """
  100.  
  101.     w = Dist.argsort(kind='mergesort')
  102.  
  103.     return y[w]
  104.  
  105.  
  106. def p_y_x_knn(y, k):
  107.     """
  108.   Funkcja wyznacza rozklad prawdopodobienstwa p(y|x) dla
  109.   kazdej z klas dla obiektow ze zbioru testowego wykorzystujac
  110.   klasfikator KNN wyuczony na danych trenningowych
  111.   :param y: macierz posortowanych etykiet dla danych treningowych N1xN2
  112.   :param k: liczba najblizszuch sasiadow dla KNN
  113.   :return: macierz prawdopodobienstw dla obiektow z X
  114.   """
  115.     NUMBER_OF_CLASSES = 4
  116.  
  117.     resized = np.delete(y, range(k, y.shape[1]), axis=1)
  118.     output = np.vstack(np.apply_along_axis(np.bincount, axis=1, arr=resized, minlength=NUMBER_OF_CLASSES + 1))
  119.     output = np.delete(output, 0, axis=1)
  120.     output = np.divide(output, k)
  121.  
  122.     return output
  123.  
  124.  
  125. def classification_error(p_y_x, y_true):
  126.     """
  127.   Wyznacz blad klasyfikacji.
  128.   :param p_y_x: macierz przewidywanych prawdopodobienstw
  129.   :param y_true: zbior rzeczywistych etykiet klas 1xN.
  130.   Kazdy wiersz macierzy reprezentuje rozklad p(y|x)
  131.   :return: blad klasyfikacji
  132.   """
  133.  
  134.     p_y_x = np.fliplr(p_y_x)
  135.     y_truea = p_y_x.shape[1] - np.argmax(p_y_x, axis=1)
  136.     y_truea = np.subtract(y_truea, y_true)
  137.     diff = np.count_nonzero(y_truea)
  138.     diff /= y_true.shape[0]
  139.  
  140.     return diff
  141.  
  142. def model_selection_knn(Xval, Xtrain, yval, ytrain, k_values):
  143.     """
  144.   :param Xval: zbior danych walidacyjnych N1xD
  145.   :param Xtrain: zbior danych treningowych N2xD
  146.   :param yval: etykiety klas dla danych walidacyjnych 1xN1
  147.   :param ytrain: etykiety klas dla danych treningowych 1xN2
  148.   :param k_values: wartosci parametru k, ktore maja zostac sprawdzone
  149.   :return: funkcja wykonuje selekcje modelu knn i zwraca krotke (best_error,best_k,errors), gdzie best_error to najnizszy
  150.   osiagniety blad, best_k - k dla ktorego blad byl najnizszy, errors - lista wartosci bledow dla kolejnych k z k_values
  151.   """
  152.     bestKIndex = 0
  153.     errors = []
  154.     sorted = sort_train_labels_knn(hamming_distance(Xval, Xtrain), ytrain)
  155.  
  156.     for i in range(len(k_values)):
  157.         error = classification_error(p_y_x_knn(sorted, k_values[i]), yval)
  158.         errors.append(error)
  159.         if (errors[bestKIndex] > error):
  160.             bestKIndex = i
  161.  
  162.     return (errors[bestKIndex], k_values[bestKIndex], errors)
  163.  
  164.  
  165. def estimate_a_priori_nb(ytrain):
  166.     """
  167.   :param ytrain: etykiety dla dla danych treningowych 1xN
  168.   :return: funkcja wyznacza rozklad a priori p(y) i zwraca p_y - wektor prawdopodobienstw a priori 1xM
  169.   """
  170.     NUMBER_OF_CLASSES = 4
  171.  
  172.     return np.divide(np.delete(np.bincount(ytrain, minlength=NUMBER_OF_CLASSES+1), 0), ytrain.shape[0])
  173.  
  174.  
  175. def estimate_p_x_y_nb(Xtrain, ytrain, a, b):
  176.     """
  177.   :param Xtrain: dane treningowe NxD
  178.   :param ytrain: etykiety klas dla danych treningowych 1xN
  179.   :param a: parametr a rozkladu Beta
  180.   :param b: parametr b rozkladu Beta
  181.   :return: funkcja wyznacza rozklad prawdopodobienstwa p(x|y) zakladajac, ze x przyjmuje wartosci binarne i ze elementy
  182.   x sa niezalezne od siebie. Funkcja zwraca macierz p_x_y o wymiarach MxD.
  183.   """
  184.     Xtrain = Xtrain.toarray()
  185.     NUMBER_OF_CLASSES = 4
  186.     rows =[]
  187.  
  188.     def summ(row, yequalk):
  189.          return np.sum(np.bitwise_and(row, yequalk))
  190.  
  191.     for i in range(1, NUMBER_OF_CLASSES+1):
  192.         yk = np.equal(ytrain, i)
  193.         yksum = np.sum(yk)
  194.         row = np.apply_along_axis(summ, axis=0, arr=Xtrain, yequalk=yk)
  195.         rows.append(np.divide(np.add(row, a - 1), yksum + a + b -2))
  196.  
  197.     return np.vstack(rows)
  198.  
  199.  
  200. def p_y_x_nb(p_y, p_x_1_y, X):
  201.     """
  202.   :param p_y: wektor prawdopodobienstw a priori o wymiarach 1xM
  203.   :param p_x_1_y: rozklad prawdopodobienstw p(x=1|y) - macierz MxD
  204.   :param X: dane dla ktorych beda wyznaczone prawdopodobienstwa, macierz NxD
  205.   :return: funkcja wyznacza rozklad prawdopodobienstwa p(y|x) dla kazdej z klas z wykorzystaniem klasyfikatora Naiwnego
  206.   Bayesa. Funkcja zwraca macierz p_y_x o wymiarach NxM.
  207.   """
  208.     X = X.toarray()
  209.  
  210.     def calc2(row, x2):
  211.         out1 = np.multiply(row, x2)
  212.         return out1
  213.  
  214.     def normalise(row):
  215.         Z = 1/np.sum(row)
  216.         return np.multiply(row, Z)
  217.  
  218.  
  219.     def calc(row):
  220.         out = X * row
  221.         out += np.negative(X) - np.negative(X) * row
  222.         out = np.apply_along_axis(np.prod, arr=out, axis=1)
  223.         return out
  224.  
  225.     test = np.apply_along_axis(calc, axis=0, arr=p_x_1_y.transpose())
  226.     test = np.apply_along_axis(calc2, axis=1, arr=test, x2=p_y)
  227.     test = np.apply_along_axis(normalise, axis=1, arr=test)
  228.  
  229.     return test
  230.  
  231.  
  232. def model_selection_nb(Xtrain, Xval, ytrain, yval, a_values, b_values):
  233.     """
  234.   :param Xtrain: zbior danych treningowych N2xD
  235.   :param Xval: zbior danych walidacyjnych N1xD
  236.   :param ytrain: etykiety klas dla danych treningowych 1xN2
  237.   :param yval: etykiety klas dla danych walidacyjnych 1xN1
  238.   :param a_values: lista parametrow a do sprawdzenia
  239.   :param b_values: lista parametrow b do sprawdzenia
  240.   :return: funkcja wykonuje selekcje modelu Naive Bayes - wybiera najlepsze wartosci parametrow a i b. Funkcja zwraca
  241.   krotke (error_best, best_a, best_b, errors) gdzie best_error to najnizszy
  242.   osiagniety blad, best_a - a dla ktorego blad byl najnizszy, best_b - b dla ktorego blad byl najnizszy,
  243.   errors - macierz wartosci bledow dla wszystkich par (a,b)
  244.   """
  245.     bestErrorIndex = 0
  246.     alen = int(len(a_values))
  247.     blen = int(len(b_values))
  248.     errors = []
  249.  
  250.     def test(index):
  251.         nonlocal bestErrorIndex
  252.         i = int(index / alen)
  253.         j = int(index % blen)
  254.  
  255.         py = estimate_a_priori_nb(ytrain)
  256.         p_x_y = estimate_p_x_y_nb(Xtrain, ytrain, a_values[i], b_values[j])
  257.         p_y_x = p_y_x_nb(py, p_x_y, Xval)
  258.         error = classification_error(p_y_x, yval)
  259.         errors.append(error)
  260.  
  261.         if (errors[bestErrorIndex] > error):
  262.             bestErrorIndex = index
  263.  
  264.     xx = map(test, range(alen * blen))
  265.     xx = list(xx)
  266.  
  267.     return  (errors[bestErrorIndex], a_values[int(round(bestErrorIndex / len(b_values)))], b_values[bestErrorIndex % len(b_values)], np.array(errors).reshape(len(a_values),len(b_values)))
  268. RAW Paste Data
  269.  
  270. from __future__ import division
  271. import scipy.spatial
  272. import numpy as np
  273.  
  274. # MATRIX + ProcessPoolExecutor
  275. def calc_distance(X, X_train):
  276.     X_train = X_train.transpose()
  277.  
  278.     outArr = X.astype(np.uint8) @ X_train.astype(np.uint8)
  279.     outArr += (~X).astype(np.uint8) @ (~X_train).astype(np.uint8)
  280.     return np.subtract(np.uint8(X_train.shape[0]), outArr)
  281.  
  282.  
  283. def hamming_distance(X, X_train):
  284.     """
  285.    :param X: zbior porownwanych obiektow N1xD
  286.    :param X_train: zbior obiektow do ktorych porownujemy N2xD
  287.    Funkcja wyznacza odleglosci Hamminga obiektow ze zbioru X od
  288.    obiektow X_train. ODleglosci obiektow z jednego i drugiego
  289.    zbioru zwrocone zostana w postaci macierzy
  290.    :return: macierz odleglosci pomiedzy obiektami z X i X_train N1xN2
  291.    """
  292.  
  293.     X = X.toarray()
  294.     X_train = X_train.toarray()
  295.  
  296.     #SCIPY
  297.     # return scipy.spatial.distance.cdist(X, X_train, metric="hamming") * X.shape[1]
  298.  
  299.     #MATRIX (uwaga na uint8 overflow)
  300.     X_train = X_train.transpose()
  301.  
  302.     outArr = X.astype(np.uint8) @ X_train.astype(np.uint8)
  303.     outArr += (~X).astype(np.uint8) @ (~X_train).astype(np.uint8)
  304.     return np.subtract(np.uint8(X_train.shape[0]), outArr)
  305.  
  306.     # MATRIX + ProcessPoolExecutor (mocno przyspiesza wykonywanie gdy duże macierze)
  307.     # MAX_PROCESSES = 4
  308.     #
  309.     # futures = []
  310.     # arrays = np.split(X, MAX_PROCESSES)
  311.     #
  312.     # from concurrent.futures import ProcessPoolExecutor, Future
  313.     # with ProcessPoolExecutor(max_workers=MAX_PROCESSES) as executor:
  314.     #     for i in range(MAX_PROCESSES):
  315.     #         futures.append(executor.submit(calc_distance, arrays[i], X_train))
  316.     #
  317.     # futures = list(map(Future.result, futures))
  318.     #
  319.     # return np.vstack(futures)
  320.  
  321. def sort_train_labels_knn(Dist, y):
  322.     """
  323.    Funkcja sortujaca etykiety klas danych treningowych y
  324.    wzgledem prawdopodobienstw zawartych w macierzy Dist.
  325.    Funkcja zwraca macierz o wymiarach N1xN2. W kazdym
  326.    wierszu maja byc posortowane etykiety klas z y wzgledem
  327.    wartosci podobienstw odpowiadajacego wiersza macierzy
  328.    Dist
  329.    :param Dist: macierz odleglosci pomiedzy obiektami z X
  330.    i X_train N1xN2
  331.    :param y: wektor etykiet o dlugosci N2
  332.    :return: macierz etykiet klas posortowana wzgledem
  333.    wartosci podobienstw odpowiadajacego wiersza macierzy
  334.    Dist. Uzyc algorytmu mergesort.
  335.    """
  336.  
  337.     w = Dist.argsort(kind='mergesort')
  338.  
  339.     return y[w]
  340.  
  341.  
  342. def p_y_x_knn(y, k):
  343.     """
  344.    Funkcja wyznacza rozklad prawdopodobienstwa p(y|x) dla
  345.    kazdej z klas dla obiektow ze zbioru testowego wykorzystujac
  346.    klasfikator KNN wyuczony na danych trenningowych
  347.    :param y: macierz posortowanych etykiet dla danych treningowych N1xN2
  348.    :param k: liczba najblizszuch sasiadow dla KNN
  349.    :return: macierz prawdopodobienstw dla obiektow z X
  350.    """
  351.     NUMBER_OF_CLASSES = 4
  352.  
  353.     resized = np.delete(y, range(k, y.shape[1]), axis=1)
  354.     output = np.vstack(np.apply_along_axis(np.bincount, axis=1, arr=resized, minlength=NUMBER_OF_CLASSES + 1))
  355.     output = np.delete(output, 0, axis=1)
  356.     output = np.divide(output, k)
  357.  
  358.     return output
  359.  
  360.  
  361. def classification_error(p_y_x, y_true):
  362.     """
  363.    Wyznacz blad klasyfikacji.
  364.    :param p_y_x: macierz przewidywanych prawdopodobienstw
  365.    :param y_true: zbior rzeczywistych etykiet klas 1xN.
  366.    Kazdy wiersz macierzy reprezentuje rozklad p(y|x)
  367.    :return: blad klasyfikacji
  368.    """
  369.  
  370.     p_y_x = np.fliplr(p_y_x)
  371.     y_truea = p_y_x.shape[1] - np.argmax(p_y_x, axis=1)
  372.     y_truea = np.subtract(y_truea, y_true)
  373.     diff = np.count_nonzero(y_truea)
  374.     diff /= y_true.shape[0]
  375.  
  376.     return diff
  377.  
  378. def model_selection_knn(Xval, Xtrain, yval, ytrain, k_values):
  379.     """
  380.    :param Xval: zbior danych walidacyjnych N1xD
  381.    :param Xtrain: zbior danych treningowych N2xD
  382.    :param yval: etykiety klas dla danych walidacyjnych 1xN1
  383.    :param ytrain: etykiety klas dla danych treningowych 1xN2
  384.    :param k_values: wartosci parametru k, ktore maja zostac sprawdzone
  385.    :return: funkcja wykonuje selekcje modelu knn i zwraca krotke (best_error,best_k,errors), gdzie best_error to najnizszy
  386.    osiagniety blad, best_k - k dla ktorego blad byl najnizszy, errors - lista wartosci bledow dla kolejnych k z k_values
  387.    """
  388.     bestKIndex = 0
  389.     errors = []
  390.     sorted = sort_train_labels_knn(hamming_distance(Xval, Xtrain), ytrain)
  391.  
  392.     for i in range(len(k_values)):
  393.         error = classification_error(p_y_x_knn(sorted, k_values[i]), yval)
  394.         errors.append(error)
  395.         if (errors[bestKIndex] > error):
  396.             bestKIndex = i
  397.  
  398.     return (errors[bestKIndex], k_values[bestKIndex], errors)
  399.  
  400.  
  401. def estimate_a_priori_nb(ytrain):
  402.     """
  403.    :param ytrain: etykiety dla dla danych treningowych 1xN
  404.    :return: funkcja wyznacza rozklad a priori p(y) i zwraca p_y - wektor prawdopodobienstw a priori 1xM
  405.    """
  406.     NUMBER_OF_CLASSES = 4
  407.  
  408.     return np.divide(np.delete(np.bincount(ytrain, minlength=NUMBER_OF_CLASSES+1), 0), ytrain.shape[0])
  409.  
  410.  
  411. def estimate_p_x_y_nb(Xtrain, ytrain, a, b):
  412.     """
  413.    :param Xtrain: dane treningowe NxD
  414.    :param ytrain: etykiety klas dla danych treningowych 1xN
  415.    :param a: parametr a rozkladu Beta
  416.    :param b: parametr b rozkladu Beta
  417.    :return: funkcja wyznacza rozklad prawdopodobienstwa p(x|y) zakladajac, ze x przyjmuje wartosci binarne i ze elementy
  418.    x sa niezalezne od siebie. Funkcja zwraca macierz p_x_y o wymiarach MxD.
  419.    """
  420.     Xtrain = Xtrain.toarray()
  421.     NUMBER_OF_CLASSES = 4
  422.     rows =[]
  423.  
  424.     def summ(row, yequalk):
  425.          return np.sum(np.bitwise_and(row, yequalk))
  426.  
  427.     for i in range(1, NUMBER_OF_CLASSES+1):
  428.         yk = np.equal(ytrain, i)
  429.         yksum = np.sum(yk)
  430.         row = np.apply_along_axis(summ, axis=0, arr=Xtrain, yequalk=yk)
  431.         rows.append(np.divide(np.add(row, a - 1), yksum + a + b -2))
  432.  
  433.     return np.vstack(rows)
  434.  
  435.  
  436. def p_y_x_nb(p_y, p_x_1_y, X):
  437.     """
  438.    :param p_y: wektor prawdopodobienstw a priori o wymiarach 1xM
  439.    :param p_x_1_y: rozklad prawdopodobienstw p(x=1|y) - macierz MxD
  440.    :param X: dane dla ktorych beda wyznaczone prawdopodobienstwa, macierz NxD
  441.    :return: funkcja wyznacza rozklad prawdopodobienstwa p(y|x) dla kazdej z klas z wykorzystaniem klasyfikatora Naiwnego
  442.    Bayesa. Funkcja zwraca macierz p_y_x o wymiarach NxM.
  443.    """
  444.     X = X.toarray()
  445.  
  446.     def calc2(row, x2):
  447.         out1 = np.multiply(row, x2)
  448.         return out1
  449.  
  450.     def normalise(row):
  451.         Z = 1/np.sum(row)
  452.         return np.multiply(row, Z)
  453.  
  454.  
  455.     def calc(row):
  456.         out = X * row
  457.         out += np.negative(X) - np.negative(X) * row
  458.         out = np.apply_along_axis(np.prod, arr=out, axis=1)
  459.         return out
  460.  
  461.     test = np.apply_along_axis(calc, axis=0, arr=p_x_1_y.transpose())
  462.     test = np.apply_along_axis(calc2, axis=1, arr=test, x2=p_y)
  463.     test = np.apply_along_axis(normalise, axis=1, arr=test)
  464.  
  465.     return test
  466.  
  467.  
  468. def model_selection_nb(Xtrain, Xval, ytrain, yval, a_values, b_values):
  469.     """
  470.    :param Xtrain: zbior danych treningowych N2xD
  471.    :param Xval: zbior danych walidacyjnych N1xD
  472.    :param ytrain: etykiety klas dla danych treningowych 1xN2
  473.    :param yval: etykiety klas dla danych walidacyjnych 1xN1
  474.    :param a_values: lista parametrow a do sprawdzenia
  475.    :param b_values: lista parametrow b do sprawdzenia
  476.    :return: funkcja wykonuje selekcje modelu Naive Bayes - wybiera najlepsze wartosci parametrow a i b. Funkcja zwraca
  477.    krotke (error_best, best_a, best_b, errors) gdzie best_error to najnizszy
  478.    osiagniety blad, best_a - a dla ktorego blad byl najnizszy, best_b - b dla ktorego blad byl najnizszy,
  479.    errors - macierz wartosci bledow dla wszystkich par (a,b)
  480.    """
  481.     bestErrorIndex = 0
  482.     alen = int(len(a_values))
  483.     blen = int(len(b_values))
  484.     errors = []
  485.  
  486.     def test(index):
  487.         nonlocal bestErrorIndex
  488.         i = int(index / alen)
  489.         j = int(index % blen)
  490.  
  491.         py = estimate_a_priori_nb(ytrain)
  492.         p_x_y = estimate_p_x_y_nb(Xtrain, ytrain, a_values[i], b_values[j])
  493.         p_y_x = p_y_x_nb(py, p_x_y, Xval)
  494.         error = classification_error(p_y_x, yval)
  495.         errors.append(error)
  496.  
  497.         if (errors[bestErrorIndex] > error):
  498.             bestErrorIndex = index
  499.  
  500.     xx = map(test, range(alen * blen))
  501.     xx = list(xx)
  502.  
  503.     return  (errors[bestErrorIndex], a_values[int(round(bestErrorIndex / len(b_values)))], b_values[bestErrorIndex % len(b_values)], np.array(errors).reshape(len(a_values),len(b_values)))
  504.  
  505.                
  506. create new paste  /  dealsnew!  /  api  /  trends  /  syntax languages  /  faq  /  tools  /  privacy  /  cookies  /  contact  /  dmca  /  scraping  /  go  
  507. Site design & logo © 2017 Pastebin; user contributions (pastes) licensed under cc by-sa 3.0 -- Dedicated Server Hosting by Steadfast   Top
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement