	function Suits()
	{
	}
	Suits.CLUBS = 0;
	Suits.SPADES = 1;
	Suits.HEARTS = 2;
	Suits.DIAMONDS = 3;
	
	function Ranks()
	{
	}
	Ranks.TWO = 0;
	Ranks.THREE = 1;
	Ranks.FOUR = 2;
	Ranks.FIVE = 3;
	Ranks.SIX = 4;
	Ranks.SEVEN = 5;
	Ranks.EIGHT = 6;
	Ranks.NINE = 7;
	Ranks.TEN = 8;
	Ranks.JACK = 9;
	Ranks.QUEEN = 10;
	Ranks.KING = 11;
	Ranks.ACE = 12;
	Ranks.JOKER = 13;

	function card(rank, suit)
	{
		this.suit = suit;
		this.rank = rank;
		
		this.toString = function()
		{
			var strRanks = new Array("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A");
			var strSuits = new Array("C", "S", "H", "D");
			
			if (this.rank == Ranks.JOKER)
				return "Joker";
			else
				return strRanks[this.rank] + strSuits[this.suit];
		}
	}
			
	function cmpCards(a, b)
	{
		if (a.rank < b.rank)
			return -1;
		else if (a.rank == b.rank)
			return a.suit-b.suit;
		else
			return 1;
	}
	
	function hand(cardCount)
	{
		this.cards = new Array(cardCount);
		
		this.ranksFreq = function()
		{
			var freq = new Array(13);
			for(var i=0; i < freq.length; i++)
				freq[i] = 0;
				
			for (var i=0; i < this.cards.length; i++)
			{
				if (this.cards[i].rank == Ranks.JOKER)
					freq[Ranks.ACE]++;
				else
					freq[this.cards[i].rank]++;
			}
			return freq;
		}

		this.isOnePair = function()
		{
			var freq = this.ranksFreq();
			
			for (var i=freq.length-1; i >= 0 ; i--)
				if (freq[i] >= 2)
					return i;
				
			return -1;
		}		
		
		this.getHighest = function(n)
		{
			var sCards = new Array(this.cards.length);
			for (var i=0; i < sCards.length; i++)
				sCards[i] = false;
				
			var highest = new Array(n);
			for (var i=0; i < n; i++)
			{
				var hCard = -1;
				for(var j=0; j < this.cards.length; j++)
					if (!sCards[j])
					{
						if (hCard == -1)
						{	
							hCard = j;
							continue;
						}
						if (this.cards[j].rank > this.cards[hCard].rank)
							hCard = j;
					}
				sCards[hCard] = true;
				highest[i] = this.cards[hCard];
			}
			return highest;
		}		
		
		this.clone = function()
		{
			var cloned = new hand(this.cards.length);
			cloned.cards = this.cards.slice(0);
			return cloned;
		}
		
		this.toString = function()
		{
			var str = "";
			for (var i=0; i < this.cards.length; i++)
				str += this.cards[i].toString() + " ";
			return str;
		}

	}

	function pokerHand()
	{
		this.isStraightFlush = function()
		{
			return ((this.isStraight() != -1) && (this.isFlush() != -1));
		}
		
		this.isNOfAKind = function(n)
		{
			var freq = this.ranksFreq();
			
			for (var i=0; i < freq.length; i++)
				if (freq[i] >= n)
					return i;
			
			return -1;
		}
		
		this.isFiveOfAKind = function()
		{
			return this.isNOfAKind(5);
		}

		this.isFourOfAKind = function()
		{
			return this.isNOfAKind(4);
		}

		this.isFullHouse = function()
		{	// Three matching cards of one rank, plus two matching cards of another rank
			var freq = this.ranksFreq();
			
			var threeOfARank = false;
			var twoOfARank = false;
			for (var i=0; i < freq.length; i++)
			{
				if (freq[i] == 3)
					threeOfARank = true;
				else if (freq[i] == 2)
					twoOfARank = true;
			}
			
			return twoOfARank && threeOfARank;
		}
		
		this.isFlush = function()
		{	// All cards of the same suit
			var suit;
			for (var i=0; i < this.cards.length; i++)
				if (this.cards[i].rank != Ranks.JOKER)
				{
					suit = this.cards[i].suit;
					break;
				}
			for (var i=0; i < this.cards.length; i++)
				if ((this.cards[i].rank != Ranks.JOKER) && (this.cards[i].suit != suit))
					return -1;
					
			var highestRank = this.getHighest(1)[0].rank;
			if (highestRank == Ranks.JOKER)
				highestRank = Ranks.ACE;
			return highestRank;
		}

		this.isStraight = function()
		{	// All cards of sequential rank
			var ranksC = new Array(5);
			var sRanks = new Array(14);
			for (var i=0; i < sRanks.length; i++)
				sRanks[i] = false;
			
			for (var i=0; i < this.cards.length; i++)
			{
				if (sRanks[this.cards[i].rank])
					return -1;
				
				sRanks[this.cards[i].rank] = true;
				ranksC[i] = this.cards[i].rank;
			}
			
			var compare = function (a, b) { return a-b; }
			ranksC.sort(compare);
			
			if (ranksC[4] == Ranks.JOKER)
			{
				if (ranksC[3] - ranksC[0] == 4)
					return ranksC[3];
				else if (ranksC[3] - ranksC[0] == 3)
				{
					if (ranksC[3] == Ranks.ACE)
						return Ranks.ACE;
					else
						return ranksC[3] + 1;
				}
				else if ((ranksC[3] == Ranks.ACE) && (ranksC[2] <= Ranks.FIVE))
					return Ranks.FIVE;
				else
					return -1;
			}
			else if (ranksC[4] - ranksC[0] == 4)
				return ranksC[4];
			else if ((ranksC[4] == Ranks.ACE) && (ranksC[3] == Ranks.FIVE))
				return Ranks.FIVE;
			else
				return -1;
		}

		this.isThreeOfAKind = function()
		{
			return this.isNOfAKind(3);
		}

		this.isTwoPair = function()
		{
			var freq = this.ranksFreq();
			
			cPairs = 0;
			for (var i=0; i < freq.length; i++)
				if (freq[i] >= 2)
				{
					cPairs++;
					if (cPairs == 2)
						return i;
				}
				
			return -1;
		}
	}
	pokerHand.prototype = new hand(5);