Pages

Tuesday, November 15, 2011

Servant (Design Pattern) in Java - example

The servant design pattern - or better idiom is used to provide the functionality (methods) to some group of objects. This functionality is common for all these object and therefor should not be repeated in every of these classes.
The object, which should be served is passed to the method of servant as a parameter. All the served objects should implement common interface - in this particular example IMovable interface. Also the type of argument passed to the servand method is of type IMovable.
The servant in this example is used to move objects from one position to another. In real life application these methods should change the position of object in small steps so that the final change would look like smooth movement (animation). In my servant method, only some message are printed instead for demonstration.
IMovable interface:
package com.shimon.servant;

import java.awt.Point;

/**
 * Movable interface
 * @author shimon
 *
 */
public interface IMovable {
 
 public void setPosition(Point p);
 
 public Point getPosition();
  
}
Implementation (Triangle.java):
package com.shimon.servant.objects;

import java.awt.Point;

import com.shimon.servant.IMovable;

public class Triangle implements IMovable {

 private int sideA;
 private int sideB;
 private int sideC;
 
 
 Point position = null;
 
 public Triangle(int sideA,int sideB,int sideC,Point p){
  this.sideA = sideA;
  this.sideB = sideB;
  this.sideC = sideC;
  this.position = p;
 }
 
 @Override
 public void setPosition(Point p) {
  this.position = p;
 }

 @Override
 public Point getPosition() {
  return this.position;
 }

 public int getSideA() {
  return sideA;
 }

 public void setSideA(int sideA) {
  this.sideA = sideA;
 }

 public int getSideB() {
  return sideB;
 }

 public void setSideB(int sideB) {
  this.sideB = sideB;
 }

 public int getSideC() {
  return sideC;
 }

 public void setSideC(int sideC) {
  this.sideC = sideC;
 }

}
Servant class (Mover.java)
package com.shimon.servant;

import java.awt.Point;

/**
 * Mover servant - moves the provided IMovable objects to specified position or increases their x an y axis position 
 * using the provided arguments.
 * @author shimon
 *
 */
public class Mover {
 
 /**
  * Private constructor
  */
 private Mover(){};
 
 /**
  * Move movable object to specified position.
  * @param moved - moved object
  * @param position - final position
  */
 public static void moveTo(IMovable moved, Point position){
  Point previousPosition = moved.getPosition();
  System.out.printf("Moving smoothly from position x=%d,y=%d to position x=%d,y=%d \n",
    previousPosition.x,
    previousPosition.y,
    position.x,
    position.y);
  moved.setPosition(position);
 }
 
 /**
  * Move movable object by specified distances.
  * @param moved - object to be moved
  * @param x - difference in x-axis
  * @param y - difference in y-axis
  */
 public static void moveBy(IMovable moved, int x, int y){
  Point previousPosition = moved.getPosition();
  System.out.printf("Moving smoothly from position x=%d,y=%d to position x=%d,y=%d \n",
    previousPosition.x,
    previousPosition.y,
    previousPosition.x + x,
    previousPosition.y + y);
  moved.setPosition(new Point(previousPosition.x + x, previousPosition.y + y));
 }
}
The servant method call is showed in the JUnit test method.
package com.shimon.servant;

import static org.junit.Assert.*;

import java.awt.Point;

import org.junit.Test;

import com.shimon.servant.objects.Rectangle;
import com.shimon.servant.objects.Square;
import com.shimon.servant.objects.Triangle;

public class MoverTest {

 @Test
 public void test() {
  Triangle triangle = new Triangle(1, 3, 2, new Point(-1,0));
  Square square = new Square(2, 4, new Point(2,5));
  Rectangle rec = new Rectangle(4, 3, new Point(-3,6));
  Mover.moveTo(rec, new Point(3,2));
  //assert equals on new position of rectangle object
  assertEquals(new Point(3,2), rec.getPosition());
  Mover.moveBy(square, 2, 4);
  assertEquals(new Point(4,9), square.getPosition());
  Mover.moveTo(triangle, new Point(-4,0));
  assertEquals(new Point(-4,0), triangle.getPosition());
 }

}
Console output:
Moving smoothly from position x=-3,y=6 to position x=3,y=2 
Moving smoothly from position x=2,y=5 to position x=4,y=9 
Moving smoothly from position x=-1,y=0 to position x=-4,y=0 

Java Crate (design pattern/idiom) example

Another example, based on example explained in the book "Navrhove vzory" (Design patterns) from Rudolf Pecionvsky. I have re-made this example just to somehow get more familiar with this design pattern (or better idiom). The "crate" is used to store the set/list of object in one place, so that the moving (passing) these objects is easier.

The example from the book is very easy, and helps to understand, how this design pattern could be applied to som very usefull application (e.g. day planner)

Code example:
 
package com.sim.crate.common;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

/**
 * The Day Plan class demonstrates the usage of crate to create simple day plan, with items that do not collide.
 * @author shimon
 *
 */
public class DayPlan {
 private final List actions = new ArrayList();
 
 /**
  * Tries to add an item to the day plan with entered start, end time and duration. Returns true, if
  * the try was successful, or false if not. The item is added only if no collision with other items in 
  * the day plan appear.
  * @param hour - start hour
  * @param minute - start minute
  * @param duration - duration
  * @param subject - item subject
  * @return - bollean true - if success, else false
  */
 public boolean add(int hour, int minute, int duration, String subject){
  int start = hour * 60 + minute;
  int end = start + duration;
  ListIterator lip = actions.listIterator();
  while(lip.hasNext()){
   Item i = lip.next();
   if (i.end <= start){
    continue;
   }
   if(i.start <= end){
    return false;
   }
  }
  lip.add(new Item(start, end, subject));
  return true;
 }
 
 /**
  * Prints all items available in the dayplan.
  */
 public void print(){
  System.out.println("\nList of actions:");
  for (Item i:actions){
   System.out.printf("%02d:%02d - %02d:%02d %s\n", i.start / 60,i.start % 60,i.end / 60,i.end % 60,i.subject);
  }
  System.out.println("--------------------------------");
 }
 
 /**
  * Inner class used to store the items.
  * @author shimon
  *
  */
 private static class Item{
  int start;
  int end;
  String subject;
  
  Item(int start, int end, String subject){
   this.start = start;
   this.end = end;
   this.subject = subject;
  }
 }
 
 public void testAdd(int h, int m, int d, String txt){
  boolean success;
  System.out.printf("Adding %d minutes from %02d:%02d for %s\n", d, h, m, txt);
  success = add(h, m, d, txt);
  System.out.println("             " + (success?"OK":"FAIL"));
 }
}

Test class:
 
package com.sim.crate.common;


import org.junit.Test;

public class DayPlanTest {

 @Test
 public void test() {
  DayPlan dp = new DayPlan();
  dp.testAdd(8, 0, 30, "Waking up");
  dp.testAdd(10, 30, 90, "Relax");
  dp.testAdd(8, 30, 30, "Breakfast");
  dp.testAdd(9, 30, 90, "Work");
  dp.print();
 }
}


Friday, November 4, 2011

Immutable object example

After reading some chapters of  the book "Design patterns" (Navrhove vzory) from czech author Rudolf Pecinovsky, I decided to test my own examples of usage of the design patterns mentioned in this book. In this first post I will publish here a code that is an example of usage of the immutable objects.

As you maybe already know - the immutable objects cannot be changed after their creation. That can be very useful in some situation. In this particular example (which is kind of a english copy of the czech example mentioned in the book "Navrhove vzory") the object, which represents fraction and its operations is created. The main thing here is, that during every operation (method) new instance of class Fraction is created instead of modifying the existing Fraction instance.

Immutable objects can be very useful in concurrent applications - their state cannot be changed, so there is no way they could be corrupted by other threads.

Code example:

 
package com.shimon.immutable;

import com.shimon.immutable.common.Functions;

/**
 * This class defines fractions and needed operations with fractions - addition, subtraction, multiplication and division.
 * @author shimon
 *
 */
public class Fraction extends Number{
 /**
  * 
  */
 private static final long serialVersionUID = 2522259263216154175L;
 private final int numerator;
 private final int denominator;
 
 /**
  * Creates new instance of class Fraction with numerator and denominator passed as parameters.
  * @param n - numerator of new fraction
  * @param d - denominator of new fraction
  */
 public Fraction (int n, int d){
  if (d == 0){
   throw new IllegalArgumentException("Denominator cannot be zero.");
  }
  int divisor = Functions.gcd(n, d);
  if (d < 0){
   n = -n;
   d = -d;
  }
  
  numerator = n / divisor;
  denominator = d / divisor;
 }
 
 public Fraction(Fraction f){
  numerator = f.numerator;
  denominator = f.denominator;
 }
 
 public Fraction(int number){
  numerator = number;
  denominator = 1;
 }
 
 /**
  * Adds a fraction passed as parameter to the Fraction instance.
  * @param f- fraction to add
  * @return
  */
 public Fraction plus(Fraction f){
  return new Fraction(numerator * f.denominator + f.numerator * denominator, denominator * f.denominator);
 }
 
 public Fraction plus (int number){
  return new Fraction(numerator + number * denominator, denominator);
 }
 
 /**
  * Substracts fraction passed as parameter from the Fraction instance
  * @param f - fraction to substract
  * @return
  */
 public Fraction minus(Fraction f){
  return new Fraction(numerator * f.denominator - f.numerator * denominator, denominator * f.denominator);
 }
 
 public Fraction minus(int number){
  return new Fraction(numerator - number * denominator, denominator);
 }
 
 /**
  * Subtracts the fraction from the number, which is passed to the method as a parameter.
  * @param number - number to subtract from
  * @return
  */
 public Fraction subtractFrom(int number){
  return new Fraction(number * denominator - numerator, denominator);
 }
 
 /**
  * Multiplies the fraction with fraction passed as parameter
  * @param f - fraction to multiply with
  * @return
  */
 public Fraction multiply(Fraction f){
  return new Fraction(numerator * f.numerator,denominator * f.denominator);
 }
 
 public Fraction multiply(int number){
  return new Fraction(number * numerator, denominator);
 }
 
 /**
  * Divides the fraction with the fraction passed as a parameter
  * @param f - fraction to divide by
  * @return
  */
 public Fraction devide(Fraction f){
  return new Fraction(numerator * f.denominator, denominator * f.numerator);
 }
 
 public Fraction devide(int number){
  return new Fraction (numerator, denominator * number);
 }
 
 /**
  * Divides the number with a fraction
  * @param number - number to be divided
  * @return
  */
 public Fraction devideNumber(int number){
  return new Fraction(denominator * number, numerator);
 }
 
 @Override
 public int intValue() {
  return numerator / denominator;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + denominator;
  result = prime * result + numerator;
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Fraction other = (Fraction) obj;
  if (denominator != other.denominator)
   return false;
  if (numerator != other.numerator)
   return false;
  return true;
 }

 @Override
 public long longValue() {
  return (long) numerator / denominator;
 }

 @Override
 public float floatValue() {
  return (float) numerator / denominator;
 }

 @Override
 public double doubleValue() {
  return (double) numerator / denominator;
 }
 
 public int getNumerator() {
  return numerator;
 }

 public int getDenominator() {
  return denominator;
 }

}

}
}
Functions class:
 
package com.shimon.immutable.common;

/**
 * This class is a library class, its methods solve the calculation of greatest common divisor and least common multiple.
 * @author shimon
 *
 */
public final class Functions {
 
 /**
  * Private constructor.
  */
 private Functions(){}
 
 /**
  * Returns greatest common divisor of two numbers.
  * @param i1 - First number
  * @param i2 - Second number
  * @return - gcd
  */
 public static int gcd(int i1, int i2){
  i1 = Math.abs(i1);
  i2 = Math.abs(i2);
  
  while (i2 > 0){
   int pom = i1 % i2;
   i1 = i2;
   i2 = pom;
  }
  return i1;
 }
 
 /**
  * Least common multiple of two numbers.
  * @param i1 - First number
  * @param i2 - Second number
  * @return lcm
  */
 public static int lcm(int i1, int i2){
  if ((i1 == 0) || (i2 == 0))
   return 0;
  return i2 * Math.abs(i1) / gcd(i1,i2);
 }
}
Test class:
 
package com.shimon.immutable;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class FractionTest {
 
 private Fraction f;

 @Before
 public void setUp() throws Exception {
  f = new Fraction(3, 5);
 }
 
 @Test
 public void testPlus(){
  Fraction result = f.plus(new Fraction(1,2));
  assertEquals(new Fraction(11,10), result);
 }
 
 @Test
 public void testMinus(){
  Fraction result = f.minus(new Fraction(2,10));
  assertEquals(new Fraction(2,5), result);
 }
 
 @Test
 public void testDivision(){
  Fraction result = f.devide(new Fraction(6,4));
  assertEquals(new Fraction(6,15), result);
 }
 
 @Test
 public void testMutliplication(){
  Fraction result = f.multiply(new Fraction(5,6));
  assertEquals(new Fraction(1,2), result);
 }

}

Thursday, October 20, 2011

Creating trigger for DB2

Recently I was solving a problem, where a trigger for DB2 database was needed. Using few of available tutorials on the internet, I started to build my first DB2 trigger. Everything seemed to be easy understand, until my first test of the trigger.
After couple minutes of googling I have eventually discovered what did the mysterious error in the console mean. The problem was in termination character for the SQL query. As the query, which should be ran after triggering event has to be terminated with a semicolon, the trigger query itself has to be terminated with something else apparently - so that the SQL engine won't start the execution after first semicolon it sees after the action query.
Solution
The solution is quiet simple - change the termination character.



--#SET TERMINATOR @
CREATE TRIGGER trg1 BEFORE INSERT ON table
FOR EACH ROW
BEGIN ATOMIC
VALUES 1; --here you can see the semicolon, which normally causes problems :)
END
@
--SET TERMINATOR ;


Some usefull links:

If you are not satisfied with the trigger you have created, you will probably think about deleting (dropping). This easy query will drop the created trigger:
DROP TRIGGER trigger_name;

PDF File
An Introduction to Trigger in DB2 for OS

Thursday, September 29, 2011

Jesenné Tatry 2011

O tom, že jeseň v horách - a to dokonca v slovenských najvyšších horách - nemusí byť len sychravá, upršaná a šedivá sme sa presvedčili počas uplynulého víkendu spolu so Zuzkou, mojimi kolegami a kamarátmi. Výhľad na všetky svetové strany, boľavé nohy a opálené tváre sú toho dôkazom.
Väčšina z nás už skúsenosti s Tatrami mala, avšak aj tak sme to s túrami nechceli príliš prehnať (v pondelok predsa len treba ísť opäť do práce). Ako jedného z najlepších kandidátov na túru sme vybrali tatranskú klasiku - Rysy
Rysy
Ubytovanie na Štrbskom plese nám zabezpečilo pohodlný ranný začiatok túry, nakoľko sme na izbu prišli už v piatok večer. V sobotu okolo osmej sme už šľapali po ceste na Popradské pleso. Túto cestu pozná snáď každý, kto podnikal nejakú tú túru do Tatier. Ako každá túra na Rysy aj táto bola vďaka nádhernému počasiu masovou akciou. Húfy ľudí sa hrnuli strminou na Chatu pod Rysmi, o vrchole ani nehovorím. V momente keď sme dorazili na vrchol to na ňom vyzera skôr ako na bidle, na ktoré si chce sadnúť priveľa vrabcov.
Slobodné kráľovstvo Rysy

Nie je sa však čomu čudovať, výhľad v takýchto dňoch je naozaj užasný, či už na ostatné tatranské štíty alebo na poľskú stranu (Morskie oko, ...). Jedine Nízke Tatry boli zahalené v oblakoch. S pomocou prísediaceho českého turistu sme na vrchole spoznali všetky podstatné tatranské končiare (je dobré stretnúť niekoho takéhoto na vrchole).
Gerlach

Nevýhodou tejto túry je podľa mňa najmä to, že jediná cesta späť (na Slovenskú stranu) je možná po tej istej ceste akou ste prišli, toto robí zostup trošku ubíjajúcim - a bolo to poznať aj na nás.
Sedlo Bystrá
Po celkom namáhavej túre na Rysy, sme prvý večer zvažovali, ktorá túra by bola vhodná na deň druhý - pôvodným plánom bola trasa z Popradského plesa po tatranskej magistrále na Batizovské a Velické pleso. Avšak odchod zo Štrbského plesa sme chceli zvládnuť do tretej hodiny poobede, takže sme sa rozhodli pre trasu - Vodopád Skok, Kozie pleso, Capie pleso, Sedlo Bystrá, Vyšné Wahlenbergovo pleso a popod Solisko až na Štrbské pleso.
Tatranská trávička

Počasie opäť nesklamalo a tak sme mali možnosť kochať sa po opustení pásma kosodreviny nádherným výhľadom na Štrbské pleso a takisto Nízke Tatry. Nasledovalo ešte prudké stúpanie do sedla Bystrá, kde sme sa jedným krokom prehupli do doliny Wahlenbergovho plesa. Ihneď sa nám naskytol ďalší nádherný výhľad na masív Kriváňu a takisto Západných tatier.
Pleso nad Skokom

Nasledovalo už len klesanie skalnatou mesačnou dolinou, miestami trochu demotivujúce, po výhľadoch, ktoré sme zažili len pár minút späť. Takto sme pomali prechádzali všetkými pásmami vysokohorskej prírody, behajúc pohľadom po okolitých svahoch, či sa nám nepodarí zbadať niekde medzi skalami poskakovať nejakého kamzíka (Zuzka tvrdila, že tu minule kamzíky boli!).
Stúpanie do sedla Bystrá

K Štrbskému plesu sa nám podarilo prísť v poriadku, pohromade a dokonca vo veľmi dobrom čase, takže sme všetci spokojní začali baliť, upratovať a chystať sa na únavnú cestu späť do Brna, ktorú nám našťastie spríjemnila zastávka na haluškách u Zuzkiných rodičov v Žiline - fantázia.

Tuesday, August 30, 2011

Resolution configaration using xrandr - Ubuntu

I was recently solving a problem with my secondary monitor screen resolution. I have 24'' 16:9 widescreen LCD monitor but the maximum resolution available in GUI monitor config tool was only 1680x1050. What I needed was 1920x1080 to make my monitor even sharper.

After some googling I have found this Ubuntu Wiki page which describes many possible display configurations using the xrandr command line tools.

Xrandr enables you to add new resolution mode for your monitor. To add new mode use this command:
xrandr --newmode "1920x1080_60.00"  172.80  1920 2040 2248 2576  1080 1081 1084 1118  -HSync +Vsync
xrandr --addmode VGA-0 1920x1080_60.00

After that, you can easily set the new resolution mode for your output device (in this case name of the device is VGA-0)
xrandr --output VGA-0 --mode 1920x1080_60.00
You can do also other stuff with xrandr command line tool (such as setting primary monitor xrandr --output VGA-0 --primary)
I would recommend to use this command line tool instead of GUI config tool.


Thursday, August 25, 2011

Moravský kras a Macocha II

O krásach Moravského krasu a najmä priepasti Macocha som sa už rozširoval v tomto príspevku. Počas návštevy mojej sestry v Brne sme sa boli na Macochu a Punkevní jaskyňu pozriet znova. O to väčšie bolo naše sklamanie, keď sme si vyšlapali cestu od zastávky Skalní mlýn až nad priepasť macocha a zistili sme, že pozrieť sa do Macochy už nie je také jednoduché ako minulý rok.
Nebránil nám v tom strach z výšky, hĺbky alebo čosi podobné. Prístup k zábradliu vrchnej lávky nad priepasťou macocha bol blokovany reťazou a brigádnikom vyberajúcim 20 Kč za nazretie do tohto krasového útvaru. Na retiazke sa krásne vynímal nápis "Pozemek obce Vílemovice".
Nebránim sa plateniu vstupného do jaskýň alebo samotnej priepasti, ale zmysel tohto poplatku mi akosi uniká.

Thursday, July 28, 2011

Cyklistika - okruh cez Ochoz u Brna

Dlhý, predlhý čas som na tento blog neprispel ani čiarkou. Ak mám povedať pravdu ani nebolo kedy, nakoľko som v júni štátnicoval, promoval a hneď na to som sa aj sťahoval. Teraz je však konečne kľud a tak by som rád napísal občas nejaký ten článoček o tom čo som zažil.

V tomto príspevku by som rád v krátkosti popísal náš (môj a Zuzkin) výlet na bicykloch v okolí Brna. Keďže sa v cyklistických trasách v okolí Brna príliš nevyznáme, vybrali sme si na začiatok niečo ľahšie - trasu o dĺžke cca 35km cez Bílovice nad Svitavou, Řičmanice, Kanice, Ochoz u Brna a cez Mariánské údolí potom späť do Líšne v Brne.

Pre lepšiu predstavu o trase prikladám aj mapku z portálu mapy.cz.
Okruh v okolí Brna (Bílovice n. Sv. - Říčmanice - Kanice - Ochoz u Br. - Mariánské údolí)

Výškový profil trasy
Nebyť nechcenej zachádzky v lesoch pri Ochoze u Brna, vyšiel by tento výlet na sto percent. Nič to však nemení na tom, že je  to veľmi príjemná cyklotúra. 

Je dosť možné, že sa takéto naše cyklovýlety budú opakovať. Samozrejme sa budem snažiť ich sem pridávať.

Monday, January 10, 2011

Ako využiť viac ako 4GB RAM vo Windows 7 (32-bit)

Väčšinou tu takéto články nepíšem, ale nakoľko sa mi táto informácia zdá byť veľmi prospešná spravím výnimku. Nedávno som si na ebay.com zakápil dva RAM moduly o veľkosti 2G (dokopy 4GB). Už som sa nevedel dočkať, kedy si ich nainštalujem do môjho notebooku, až kým som ich do notebooku fyzicky nevložil a čuduj sa svete - systém síce našiel 4GB RAMky, avšak použiť dokázal 2.99GB.

Používam Windows 7 Professional a už pri kúpe RAMiek som si samozrejme zistoval, či takéto mnostvo pamäte bude schopný môj systém naadresovať. Na tejto stránke môžete vidieť, že limit pre Win 7 Professional je 4GB. Tak prečo tých 2,99 použiteľných GB?

Definitívnu odpoveď na moju otázku mi po chvíli hľadania poskytla až táto stránka. Problém (aj môj) je ten, že Windows používa ten istý adresný priestor pre RAMku ako aj pre grafickú kartu či zdroje matičnej dosky. V praxi to znamená, že po tom, čo systém naadresuje tieto zdroje, ostane mu dostatok adries len na adresovanie 2,99GB RAM pamäte.

Našťastie exituje riešenie tohoto problému. Na prvý pohľad síce vyzerá trochy pochybne nakoľko ho vyvinuli ruský programátori a na na stránke tejto systémovej záplaty sa nachádza niekoľko odkazov s erotickým obsahom, avšak účinné je na 100%.

Táto záplata (patch) využíva to, že kernel Windowsu 2003 Datacenter Edition umožňuje adresovanie až 64 GB RAM pamäte. Je to dosť zaujímave a ja osobne v tom vidím komerčné zámery Microsoftu - nakoľko sa takisto ako u Windows 7 Professional jedna o 32-bitový operačný systém. Takže adresovanie takéhoto priestoru je možné. No ale späť k záplate - jednoducho vám tento patch pridá nový kernel súbor a pridá ho ako novú možnosť štartu systému. Po tejto záplate by ste mali byťschopný využiť až do 64 GB RAM pamäte(teda váš systém). U mňa to fungovalo.

Saturday, January 1, 2011

Vítanie nového roku v Brne

Tento rok sme Silvester so Zuzkou strávili v Brne. No a aj kvôli môjmu prechladnutiu sme si ho príliš neužili. Síce sme sa vybrali pred polnocou do mesta pozrieť sa na ohňostroje, no nič špeciálne sme nevideli, pretože očakávaný ohňostroj nad Špilbergom sa nekonal.

Dnes sme však zistili, že ohňostroj sa chystal až na oslavu nového roku, nie na Silvestra. Tak sme sa do mesta vybrali znova. Tento raz úspešne. Musím priznať, že krajší ohňostroj som ešte v živote nevidel. Celý program trval možno 15 minút. Prestriedalo sa množstvo rôznych typov ohňostrojov. Na mňa osobne najviac spravili dojem ohňostroje, ktore vyleteli vysoko do vzduchu a následne posiali celú oblohu nad mestom obrovským množstvom padajúcich hviezdičiek.

Prikladám zopár (nie príliš dobrých) fotiek.




A ešte aj video:
Lepšie je však zažiť to celé naživo.