Zebra puzzle

You are encouraged to solve this task according to the task description, using any language you may know.
The Zebra puzzle, a.k.a. Einstein's Riddle,
is a logic puzzle which is to be solved programmatically.



It has several variants, one of them this:

   There are five houses.
   The English man lives in the red house.
   The Swede has a dog.
   The Dane drinks tea.
   The green house is immediately to the left of the white house.
   They drink coffee in the green house.
   The man who smokes Pall Mall has birds.
   In the yellow house they smoke Dunhill.
   In the middle house they drink milk.
   The Norwegian lives in the first house.
   The man who smokes Blend lives in the house next to the house with cats.
   In a house next to the house where they have a horse, they smoke Dunhill.
   The man who smokes Blue Master drinks beer.
   The German smokes Prince.
   The Norwegian lives next to the blue house.
   They drink water in a house next to the house where they smoke Blend. 

The question is, who owns the zebra?

Additionally, list the solution for all the houses.

Optionally, show the solution is unique.



Related tasks

   Dinesman's multiple-dwelling problem
   Twelve statements


var people,drinks,houses,smokes,pets; // lists treated as associated arrays
fcn c2 { people.find(English)==houses.find(Red) }
fcn c3 { people.find(Swede)==pets.find(Dog) }
fcn c4 { people.find(Dane)==drinks.find(Tea) }
fcn c5 { (houses.find(Green) + 1)==houses.find(White) }
fcn c5a{ houses.find(Green)!=4 }	// deduced constraint (from c5)
fcn c5b{ houses.find(White)!=0 }	// deduced constraint (from c5)
fcn c6 { drinks.find(Coffee)==houses.find(Green) }
fcn c7 { smokes.find(PallMall)==pets.find(Bird) }
fcn c8 { houses.find(Yellow)==smokes.find(Dunhill) }
fcn c9 { drinks[2]==Milk }  // 0,1,2,3,4
fcn c10{ people[0]==Norwegian }
fcn c11{ (smokes.find(Blend) - pets.find(Cat)).abs()==1 }
fcn c12{ (pets.find(Horse) - smokes.find(Dunhill)).abs()==1 }
fcn c13{ smokes.find(BlueMaster)==drinks.find(Beer) }
fcn c14{ people.find(German)==smokes.find(Prince) }
fcn c15{ (people.find(Norwegian) - houses.find(Blue)).abs()==1 }
fcn c16{ (drinks.find(Water) - smokes.find(Blend)).abs()==1 }
#<<<#//////////////////////////////////////////////////////////////////////
Showing a solution to c2,c5,c10,c15:
	|0	   1       2         3         4
--------+-------------------------------------------
houses:	|Yellow    Blue    Red       Green     White
people:	|Norwegian Dane    English   German    Swede
#<<<#//////////////////////////////////////////////////////////////////////
 
const Blue =0,Green     =1,Red    =2,White    =3,Yellow=4,
      Dane =0,English   =1,German =2,Norwegian=3,Swede =4,
      Beer =0,Coffee    =1,Milk   =2,Tea      =3,Water =4,
      Blend=0,BlueMaster=1,Dunhill=2,PallMall =3,Prince=4,
      Bird =0,Cat       =1,Dog    =2,Horse    =3,Zebra =4;
perm5:=T(0,1,2,3,4) : Utils.Helpers.permute(_); // 120 sets
 
constraints:=T(c2,c3,c4,c5,c5a,c5b,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16);
constraints1:=T(c2,c5,c10,c15); // houses,people: 12 solutions
constraints2:=T(c4,c6,c9);      // houses,people,drinks: down to 8 solutions
foreach _houses,_people in (perm5,perm5){ houses,people=_houses,_people;
   if(not constraints1.runNFilter(False)){ // all constraints are True
      foreach _drinks in (perm5){ drinks=_drinks;
	 if(not constraints2.runNFilter(False)){
	    foreach _smokes,_pets in (perm5,perm5){ smokes,pets=_smokes,_pets;
	       if(not constraints.runNFilter(False)) printSolution();
	    }// smokes,pets
	 }
      } // drinks
   } // houses,people
}
fcn printSolution{
   var titles=T("Houses:","People:","Drinks:","Smokes:","Pets:"),
       names=T(
          T("Blue", "Green",      "Red",    "White",    "Yellow",),
	  T("Dane", "English",    "German", "Norwegian","Swede",),
	  T("Beer", "Coffee",     "Milk",   "Tea",      "Water",),
	  T("Blend","Blue Master","Dunhill","Pall Mall","Prince",),
	  T("Bird", "Cat",        "Dog",    "Horse",    "Zebra",) ),
   ;
   fmt:=("%-7s " + "%-11s "*5).fmt;
   foreach list,title,names in (T(houses,people,drinks,smokes,pets)
	   .zip(titles,names))
      { println(list.apply(names.get):fmt(title,_.xplode())) }
}