Sunday 26 November 2017

Moving average scala


W ten weekend postanowiłem spróbować mojej ręki na jakiejś Scali i Clojure Jestem biegła w programowaniu obiektowym, a więc Scala była łatwa do pobrania jako język, ale chciała wypróbować funkcjonalne programowanie To tam stało się trudne. t wydaje się, że moja głowa w tryb pisania funkcji Jako ekspert programowania funkcjonalnego, jak podejście problemu. Zapewnia listę wartości i określonego okresu sumowania, w jaki sposób można wygenerować nową listę prostej średniej ruchomej lista. Na przykład Biorąc pod uwagę listę wartości 2 0, 4 0, 7 0, 6 0, 3 0, 8 0, 12 0, 9 0, 4 0, 1 0, a okres 4, funkcja powinna zwrócić 0 0 , Po czym spędziłem dzień, w którym mogłem spróbować, najlepszym, co mogłem wymyślić w Scali, było to, wiem. to jest strasznie nieefektywne, I d raczej zrobić coś like. Now, że byłoby łatwo zrobić w imperatywnym stylu, ale mogę t dla życia mnie, jak to wyrazić funkcjonalnie. Interesting problemem mogę myśleć o ma Rozwiązania ny z różnym stopniem skuteczności Po dodaniu rzeczy wielokrotnie nie jest to naprawdę problem z wydajnością, ale niech przyjmiemy, że jest równe, zero na początku może być wcześniej dodawane, więc niech się martwić o ich produkcję Jeśli algorytm dostarcza to oczywiście, dobrze, jeśli nie, poprawimy to później. Uruchomienie programu Scala 2 8 spowoduje, że następny wynik dałby wynik n przez użycie przesuwania, aby uzyskać okno przesuwne listy. Niemniej jednak jest to dość eleganckie, ale nie mieć najlepszą wydajność, ponieważ nie korzysta z już obliczonych dodatków Więc, mówiąc o nich, jak możemy je dostać. Powiedzmy, że piszemy to. Mamy listę sumy każdej pary. Spróbujmy użyj tego wyniku do obliczenia średniej ruchomej z 4 elementów Powyższa formuła spowodowała następujące obliczenia. So, jeśli weźmiemy każdy element i dodajemy go do drugiego następnego elementu, otrzymamy średnią ruchomej dla 4 elementów. Możemy to zrobić w ten sposób. Następnie możemy obliczyć średnią ruchomej dla 8 elementów, itd. Cóż, jest znany algorytm obliczania rzeczy, które następują po takim wzorze. Najbardziej znany jest ze względu na jego wykorzystanie do obliczania mocy liczb. To idzie w ten sposób. , tu okres logiki 0 jest nieważny, okres 1 jest równy wejściu, okres 2 przesuwa się do okna o wielkości 2 Jeśli większy od tego, może być parzysty lub nieparzysty. Jeśli nieparzyste, dodajemy każdy element do ruchuSum next dziwne - 1 elementy Na przykład, jeśli 3, dodajemy każdy element do ruchomych szesnastu kolejnych 2 elementów. Jeśli nawet obliczymy ruchSum dla n 2, a następnie dodajemy każdy element do jednego n 2 kroków po tym. Z tą definicją, możemy następnie wrócić do problemu i zrobić to. There sa niewielkie nieefektywności w odniesieniu do wykorzystania, ale to okres O, nie Może być bardziej efektywny z funkcji recursive ogon I oczywiście definicji przesuwania I pod warunkiem jest horrendous wydajność-wise, ale będzie o wiele lepsze definicji na Scala 2 8 Należy zauważyć, że możemy zrobić t skuteczny sposób przesuwu na liście, ale możemy to zrobić na Iterable. Having powiedział wszystko, I d go z pierwszą definicją i optymalizacji tylko jeśli krytyczna ścieżka analizy wskazał to jako wielki. Do podsumowania, rozważyć jak poszedłem o problem Mamy ruchomy średni problem Średnia ruchoma to suma poruszającego się okna na liście podzielonego przez rozmiar tego okna Więc najpierw spróbuję dostać okno przesuwne, sumować wszystko, a następnie podzielić przez rozmiar. Następnym problemem było uniknąć powtórzenia już obliczonych dodatków W tym przypadku udałem się do możliwie najmniejszego dodatku, i próbowałem dowiedzieć się, jak obliczyć większe sumy ponownego wykorzystania takich wyników. Wreszcie, spróbujmy rozwiązać problem, jak to sobie wyobraziłeś, dodając i odejmując od poprzedniego wyniku Pierwsze przeciętne jest łatwe. Num robimy dwie listy Najpierw lista elementów do odejmowania Następna lista elementów, które mają zostać dodane. Możemy dodaj te dwie listy przy użyciu kodu pocztowego Ta metoda będzie tylko prod uce jak wiele elementów, jak mała lista, co pozwala uniknąć problemu z odejmowaniem większy niż needed. We zakończyć przez komponowanie wyniku z fold. which jest odpowiedzią na zwrócenie Cała funkcja wygląda tak. Nie wiem Clojure lepiej niż Scala, więc tutaj idzie Kiedy piszę to, inne wejście z Clojure jest koniecznością, że nie jest to, co masz po i nie jest idiomy Clojure Pierwszy algorytm, który przychodzi mi do głowy, powtarza wielokrotnie żądaną liczbę elementów z sekwencji, upuszczając pierwszy element i powtarzające się. Następujące prace na dowolnym wektorze lub liście sekwencji, leniwe lub nie i daje leniwą sekwencję średnich --- które mogą być pomocne, jeśli pracujesz nad listą na czas nieokreślony Zauważ, że to trwa dbanie o przypadek bazowy przez domyślne zwrócenie zero, jeśli nie ma wystarczająco dużo elementów na liście do konsumpcji. Wyprzedzaj to na swoich danych testowych. Nie daje 0 dla pierwszych kilku elementów w sekwencji, chociaż można to łatwo obsługiwać co sztucznie. Najprostszą rzeczą jest, aby zobaczyć wzór i być w stanie przypomnieć, że dostępna funkcja, która pasuje do partycji rachunku daje leniwy widok części sekwencji, które możemy następnie mapować over. Someone poprosił o ogon recursive rekurencyjna wersja recursion ogonem vs lenistwo jest trochę kompromisu Kiedy twoja praca tworzy listę, a następnie rekursywna rekurencyjna funkcja jest zazwyczaj bardzo prosta, a to nie jest wyjątek --- po prostu buduj listę jako argument do podfunkcji Będziemy zbierać się do wektora zamiast listy, ponieważ w przeciwnym razie lista zostanie zbudowana do tyłu i musi być odwrócona na końcu. loop jest sposobem na anonimowy wewnętrzny rodzaj funkcji, jak Schemat s o nazwie let reur musi być używany w Clojure, aby wyeliminować ogonowe wywołania conj jest uogólnione minusy dołączone w sposób naturalny dla kolekcji --- początek list i koniec wektory. answer 24 sierpnia 09 w 2 58. Zdecydowałem się dodać do tego starego Q, ponieważ temat pojawił się ponownie i f Zalecamy wskazanie tej ładnej kolekcji możliwych rozwiązań, dodając własne podejście, które różni się od wcześniejszych wersji w Clojure, jak wyjaśniono w "Być może uda nam się stworzyć najbardziej kompletną repozytorium funkcjonalnych implementacji" mov-avg "- Micha Marczyk Marzec 2 10 w 0 20.Here sa częściowo wolne od punktu wolnego jeden wiersz Haskell solution. First stosuje ogonki do listy, aby uzyskać listę ogonów, więc. Reverses go i spada pierwsze pozycje p biorąc p jako 2 here. In przypadku aren t znany z symbolu kropki, jest operatorem dla funkcjonalnego składu, co oznacza, że ​​przekazuje on wyjście jednej funkcji jako dane wejściowe innego, komponując je w pojedynczą funkcję gf oznacza uruchomienie f na wartość, a następnie przekazanie wyjścia do g , więc fgx jest taki sam jak gfx Ogólnie jego użycie prowadzi do bardziej przejrzystego stylu programowania. Następnie odwzorowuje funkcję z sumy sumy sumy p podjąć na listę Tak więc dla każdej listy na liście ma pierwsze elementy p, sumuje je, a następnie dzieli Później po prostu odwróćmy listę z powrotem na odwrót. Wszystko to wygląda o wiele bardziej nieefektywne, niż jest odwrotne, nie fizycznie odwraca kolejność listy, dopóki lista nie zostanie oszacowana, po prostu wypisuje ją na stos dobry ol leniwy ogon Haskell również nie robi wszystkich tych oddzielnych list, po prostu odwołuje się do różnych sekcji oryginalnej listy To nadal nie jest świetnym rozwiązaniem, ale to jedna linia long. Here sa nieco ładniejsze, ale dłużej rozwiązanie, które używa MapAccum zrobić przesuwne odejmowanie i addition. First podzielimy listę na dwie części na p, więc. Zacznij od pierwszego bit. Zknij drugą bitę z oryginalną listą, która właśnie sparowuje pozycje w kolejności z dwóch list Oryginalna lista jest oczywiście dłuższa, ale stracimy ten dodatkowy kawałek. Teraz definiujemy funkcję dla naszej mapAccum ulator mapAccumL jest taka sama jak mapa, ale z dodatkowym parametrem akumulatora stanu pracy, który jest przekazywany z poprzedniego mapowania do następnego, gdy mapa przebiega przez listę Używamy akumulatora jako naszej średniej ruchomej , i jak nasza lista jest utworzona z elementu, który właśnie opuścił okno przesuwne i element, który właśnie wprowadził go w liście po prostu zip, nasza funkcja przesuwania zajmuje pierwszą liczbę x od średniej i dodaje drugą liczbę y wtedy przekazać nowe s wzdłuż i zwrócić s podzielone przez p snd sekunda bierze tylko drugiego członka krotki pary, która służy do podjęcia drugiej wartości zwracanej przez mapAccumL, ponieważ mapAccumL zwróci akumulator, a także odwzorowaną mapę. nie znasz symbolu, który jest operatorem aplikacji Nie robi nic naprawdę, ale ma niski, prawoskrętny wiążący priorytet, więc oznacza to, że możesz pominąć nawiasy pamiętać LISPers, iefx jest taki sam jak f x. Running ma 4 2 0, 4 0, 7 0, 6 0, 3 0, 8 0, 12 0, 9 0, 4 0, 1 0 daje 4 75, 5 0, 6 0, 7 25, 8 0, 8 25, 6 5 dla każdego rozwiązania. Oh i musisz zaimportować Listę modułów, aby skompilować dowolne rozwiązanie. Daniel Dzięki Kod kreskowy jest o wiele prostszy niż objaśnienie - opisałeś dokładnie to, co masz na liście Dwóch listów Strumienie są zachowywane w obu funkcjach i odbieraj swoje głowy podczas każdego iterowania Jedna lista strumieni służy jako główna kolekcja do przechodzenia przez kolejne, podczas gdy inne Strumień listy, który jest tą samą kolekcją, z wyjątkiem okresu krótszego niż go dwukrotnie, jest wykorzystywany przy obliczaniu nowej średniej ruchomej Walter Chang 24 sierpnia 2009 r. 17 19. Język programowania J ułatwia takie programy, jak średnia ruchoma Rzeczywiście, istnieją mniej znaków niż w ich etykiecie, średniej ruchomej. Dla wartości podanych w tym pytaniu, w tym wartości nazw, jest prosty sposób na kodowanie. Możemy to opisać przy użyciu etykiet dla komponentów. W obu przykładach użyto dokładnie tego samego programu różnica polega na tym, że użyto więcej nazw w drugim formularzu. Takie nazwy mogą pomóc czytelnikom, którzy nie znają primaries. Lets J. Spójrzmy nieco dalej na to, co się dzieje w podprogramie, średnio d enotes summation i oznacza podział tak, jak klasyczny znak Obliczenie liczbie punktów jest dokonywane przez program ogólny, to jest suma wartości podzielona przez liczbę wyników. Wynik obliczonej średniej ruchomej kalkulacji nie obejmuje zerowe zera oczekujące w pierwotnym pytaniu Te zera są prawdopodobnie nie częścią zamierzonego kalkulatora. Technika stosowana tutaj jest nazywana cichym programowaniem. Jest prawie taka sama, jak pozbawiona sensu styl programowania funkcjonalnego. 26 sierpnia 10 w 16 15. Oto Clojure udając, że jest bardziej funkcjonalnym językiem Jest to pełny zwrotnik, btw, i zawiera wiodące zera. kończąc robi to w tym przypadku, to nie robi naprawdę sprawy, jakiego porządku parametrów go. answered 24 sierpnia 09 w 4 56.Hi Jonathan, I'm pretty nowy do tego funkcjonalnego programowania, mógłbyś proszę wyjaśnić mi, jak jest rekursywne-po-wtórne Dzięki James P 24 sierpnia 09 w 14 38. Rekursja dzieje się na if oświadczenie, gdzie każda opcja jest oparta na powtórzyć To obliczy każdy parametr pierwszy, a dopiero wtedy recurse Odpowiedź będzie wynik recur As wynik jest taki sam wynik zwrócony przez rekurencję, bez żadnych innych obliczeń, jest to rekurencyjny rekord Daniel C Sobral 24 sierpnia 09 15 20. Ten przykład wykorzystuje stan, ponieważ dla mnie to pragmatyczne rozwiązanie w tym przypadku, a zamknięcie, aby utworzyć funkcję uśredniania windowing. It jest nadal funkcjonalny w sensie korzystania z pierwszej klasy funkcji, choć nie jest efektem ubocznym wolne Dwa języki, o których wspomniano zarówno uruchomić na górze JVM i tym samym pozwalają na state - kierownictwo w razie potrzeby. przy odpowiedzi 24 sierpnia 09 w 1 55. Rozwiązanie to jest w Haskell, które jest bardziej znany me. po odpowiedzi 24 sierpnia 09 w 10 23.I jak użycie instrukcji meczu próbowałem coś podobnego, ale couldn t całkiem robię to aż tam James P 24 sierpnia 09 w 14 39. Krótka wersja programu Clojure, która ma tę zaletę, że ma długość listy O, niezależnie od okresu. Wykorzystuje to fakt, że można obliczyć sumę zakresu liczb, tworząc skumulowaną sumę sekwencji, np. 1 2 3 4 5 - 0 1 3 6 10 15, a następnie odejmując dwie cyfry z przesunięciem równym Twojemu okresowi. Spóźnienie na imprezie, a także nowe funkcjonalne programowanie, przyszedłem do tego rozwiązania z wewnętrzną funkcją. Przyjąłem ten pomysł, podzielić całą listę przez len przed wyprzedzeniem I wtedy wygeneruję sumę na początek dla elementów-len-first I wygeneruję pierwsze, nieprawidłowe elementy 0 0, 0 0. Następnie rekurencyjnie odejmujemy pierwszy i dodaj ostatnią wartość W końcu usłyszysz całą rzecz. Odpowiedzi 29 kwietnia 10 w 19 28. W pseudokodie Haskella. Teraz należy naprawdę abstrakcyjnie wyliczyć 4 odpowiedzi. 23 lipca 13 na 13 45. Kluczem jest funkcja ogonów, która odwzorowuje listę na listę kopii pierwotnej listy, a właściwość n-tego elementu wyniku brakuje pierwszych elementów n-1. Stosujemy średnią fg do osiągnięcia wyniku, co oznacza, że ​​przyjmujemy prefiks długości n z podlisty i obliczy jego średnią. Jeśli długość listy jest średnia, to nie n, to nie obliczymy średniej, ponieważ jest niezdefiniowana W tym przypadku zwracamy Nothing Jeśli tak jest, robimy to i owijmy w Just Finally, uruchamiamy catMaybes w wyniku fmg avg weź n, aby pozbyć się Maybe type. answered Październik 21 13 w 1 29. byłem zaskoczony i rozczarowany wykonaniem tego, co wydawało mi się najbardziej idiomatycznymi rozwiązaniami Clojure, rozwiązaniami JamesCunningham s lazy-seq. Oto połączenie rozwiązania Jamesa z myślą o przystosowaniu szybkich, potęgi do przesunięcia sumy. Edytuj ten jeden - w oparciu o rozwiązanie mikera - jest nawet szybsze. odpowiedzi Jul 22 13 at 19 21.Your Odpowiedź.2017 Stack Exchange, Inc. Introduced in Spark 1 4, funkcje okna Spark poprawiły wyrazistość Spark DataFrames i Spark SQL Dzięki funkcjom okienkowym można łatwo obliczyć średnią ruchomej lub sumę skumulowaną lub odwoływać się do wartości w poprzednim rzędzie tabela Funkcje okien umożliwiają wykonywanie wielu typowych obliczeń za pomocą ramek danych, bez konieczności korzystania z manipulacji RDD. Rozwiązania, funkcje UDF i funkcje okien. Funkcje wierszy są komplementarne do istniejących agregatów operacji DataFrame, takich jak suma i średnia oraz UDF. Przegląd, agregaty obliczyć jeden wynik, sumę lub średnią dla każdej grupy wierszy, podczas gdy UDF obliczyć jeden wynik dla każdego wiersza w oparciu o tylko dane w tym wierszu W przeciwieństwie do funkcji okna obliczyć jeden wynik dla każdego wiersza w oparciu o okno wierszy Na przykład, w średniej ruchomej, obliczysz dla każdego wiersza średnią wierszy otaczających bieżący wiersz można to zrobić z funkcjami okna. Moving Średnia przykład. Zejdź nurkować bezpośrednio do m średnie przykłady W tym przykładowym zestawie danych są dwa klienci, którzy codziennie spędzili różne kwoty pieniędzy. Budowanie klienta DataFrame Wszystkie przykłady są zapisywane w programie Scala za pomocą Spark 1 6 1, ale to samo można zrobić w klientach Pythona lub SQL. val 2018-05-01, 50 00. Alice, 2018-05-03, 45 00. Alice , 2018-05-04, 55 00. Bob, 2018-05-01, 25 00.Window funkcji i definicji okien Spec. Jak pokazano w powyższym przykładzie, istnieją dwie części do zastosowania funkcji okna 1 określającej funkcję okna, np. avg w przykładzie i 2 określające specyfikację okna lub wSpec1 w przykładzie Dla 1 można znaleźć pełną listę funkcji okna Tutaj można używać funkcji wymienionych w sekcji Funkcje agregujące i funkcje okien. Aby określić 2 okno spec, są trzy partycje partycji przez, order by i frame. Partition definiuje, jak dane są pogrupowane w powyższym przykładzie, to przez klienta Musisz określić rozsądne grupy, ponieważ wszystkie dane w grupie zostaną zebrane do tej samej maszynie, ramka DataFrame została już podzielona na pożądaną grupę definiuje sposób, w jaki wiersze są porządkowane w obrębie grupy w powyższym przykładzie, według daty. Frame definiuje granice okna względem bieżącego wiersza w powyższym przykładzie, okno znajdowało się między poprzednim wierszem a następnym wierszem. Kumulowana suma. Następnie obliczmy skumulowaną sumę kwoty wydanej na klienta. Okno określa zakres ramek od początku do bieżącego wiersza 0.val wSpec2 0. Utwórz nową kolumnę, która oblicza sumę w zdefiniowanej ramce okna. Średnia Średnia średnica ruchoma. Średnia Średnia średniej ruchomej Zachęcamy do rozwiązania tego zadania zgodnie z opis zadania, przy użyciu dowolnego języka, który można znać przy użyciu prostej średniej ruchomej szeregu liczb. Stwórz instancję klasy stanu, która zajmuje pewien okres i zwraca procedurę, która przyjmuje liczbę jako argument i zwraca prostą średnią ruchową jej argumentów daleką. Prosta średnia ruchoma to metoda obliczania średniej strumienia liczb przez uśrednienie tylko ostatnich liczb P ze strumienia, gdzie P znany jest jako okres. Może być zaimplementowany poprzez wywołanie procedury inicjowania z P jako jej argument, IP, który powinien następnie zwracać procedurę, która po wywołaniu z poszczególnymi, kolejnymi członkami strumienia liczb, oblicza średnią do, a ostatnia z nich P, pozwala na wywołanie tego SMA. Słowo s misterny w opisie zadań odnosi się do potrzeby, aby SMA zapamiętała pewne informacje między połączeniami do niego. Okres, P. Załadowany kontener co najmniej ostatnich numerów P z każdego indywidualnego wywołania. Stateful oznacza również, że kolejne wezwania do I , inicjator powinien zwracać oddzielne procedury, które nie udostępniają zapisanego stanu, dzięki czemu mogą być używane na dwóch niezależnych strumieniach danych. Pseudo-kod implementacji SMA. Ta wersja używa stałej kolejki do przechowywania ostatnich wartości p funkcja zwracana z init-moving-average ma swój stan w atomie posiadającym wartość kolejki. Implementacja ta wykorzystuje listę okrągłą do przechowywania liczb w oknie na początku każdego wskaźnika iteracji odnosi się do komórki listy, która zachowuje wartość tylko przenoszącą poza oknem i zastąpienie go wartością dodaną. Użycie edytora Closure. Aktualnie ten sma może być nogc, ponieważ przydziela on zamknięcie na stercie Niektóre analizy ucieczki mogą usunąć alokację sterty. Korzystanie z edycji Struct. Ta wersja unika się przydziału sterty zamknięcia, przechowującego dane w ramce stosu funkcji głównej. Tego samego. Aby uniknąć zbliżania się liczby zmiennoprzecinków do zestrojenia się i wzrastania, kod mógłby wykonywać okresową sumę na całość okrągła kolejka array. This wdrożenia produkuje dwa obiekty współdzielenia funkcji Idiomatic jest w E oddzielne wejście od wyjściowego odczytu z zapisu, a nie łącząc je w jeden obiekt. The struktury jest taki sam jak wdrożenie odchylenia standardowego E. Aleksir program poniżej generuje anonimową funkcję z osadzonym okresem p, która jest używana jako okres prostej średniej ruchomej Funkcja runa odczytuje dane liczbowe i przekazuje ją do nowo utworzonej funkcji anonimowej, a następnie sprawdza wynik na STDOUT. Wyjście jest pokazane poniżej , ze średnią, a następnie przez zgrupowane dane wejściowe, tworząc podstawę każdej średniej ruchomej. Erlang ma zamknięcia, ale zmienne niezmienne Rozwiązaniem jest wtedy użycie proces ses i prosta wiadomość oparta na językach API. Matrix mają procedury służące do obliczania średniej szybkości dla danej sekwencji elementów. Jest mniej skuteczne w pętli, jak w następujących komendach. Ciągle informuje o wejściu I, który jest dodawany do końca listę L1 L1 można znaleźć naciskając 2ND 1, a średnia można znaleźć na liście OPS. Press ON, aby zakończyć program. Funkcję zwracającą listę zawierającą uśrednione dane dostarczonego argumentu. Program, który zwraca prostą wartość na każdym lista invocation. list to uśredniona p to okres 5 zwraca uśrednioną listę. Przykład 2 Użycie programu movinav2 i, 5 - Inicjalizacja średniej ruchomej i zdefiniowanie okresu 5 movinav2 3, xx - nowe dane w wartości listy 3 , a wynik zostanie zapisany na zmiennej x, a następnie wyświetli się movinav2 4, xx - nowa wartość danych 4, a nowy wynik zostanie zapisany na zmiennej x, a na wyświetlaczu pojawi się 4 3 2. Opis funkcji movinavg variable r - jest wynikiem uśredniona lista, która będzie zwrócona zmienna i - jest zmienną indeksową i wskazuje na koniec listy podrzędnej listę będącą uśrednioną zmienną z - zmienną pomocniczą. Funkcja wykorzystuje zmienną i do określenia, które wartości listy zostaną uwzględnione w kolejnej średniej obliczenie W każdej iteracji zmienna i wskazuje na ostatnią wartość na liście, która będzie używana w przeciętnym obliczeniu Więc musimy tylko ustalić, która będzie pierwszą wartością na liście Zazwyczaj będziemy musieli rozważyć elementy p, więc Pierwszym elementem będzie indeksowany przez ip 1 Jednakże w pierwszych iteracjach, że obliczanie będzie zazwyczaj ujemne, więc poniższe równanie unika ujemnych indeksów max ip 1,1 lub, układając równanie, max ip, 0 1 Ale liczba elementy na pierwszych iteracjach również będą mniejsze, prawidłowa wartość będzie indeksem końcowym - zacznij indeks 1 lub, układając równanie, i - max ip, 0 1 1, a następnie, i-max ip, 0 zmienna z zachowuje wspólny wartość max ip, 0 więc beginindex będzie z 1 i numberofelements będzie z iz. mid list, z 1, iz zwróci listę wartości, która będzie sumą uśrednioną sumuje je suma rri będzie je przeciętnie i zapisać wynik w odpowiednim miejscu na liście wyników. fp1 tworzy częściową aplikację ustalając w tym przypadku parametry drugie i trzecie.

No comments:

Post a Comment