void CSymbolEngineOpenPPLHandAndBoardExpression::ResetOnHeartbeat() {
	_prime_coded_hole_cards = PrimeCodedRanks(
    p_table_state->User()->_hole_cards[0].GetOpenHoldemRank(),
    p_table_state->User()->_hole_cards[1].GetOpenHoldemRank());
	_prime_coded_board_cards = PrimeCodedRanks(
    p_table_state->_common_cards[0].GetOpenHoldemRank(),
    p_table_state->_common_cards[1].GetOpenHoldemRank(),
    p_table_state->_common_cards[2].GetOpenHoldemRank(),
    p_table_state->_common_cards[3].GetOpenHoldemRank(),
    p_table_state->_common_cards[4].GetOpenHoldemRank());
  write_log(preferences.debug_hand_and_baord_expressions(), 
		"[CSymbolEngineOpenPPLHandAndBoardExpression] _prime_coded_hole_cards = %i\n",
		_prime_coded_hole_cards);
	write_log(preferences.debug_hand_and_baord_expressions(), 
		"[CSymbolEngineOpenPPLHandAndBoardExpression] _prime_coded_board_cards = %i\n",
		_prime_coded_board_cards);
}
void CSymbolEngineOpenPPLHandAndBoardExpression::ResetOnHeartbeat() {
	_prime_coded_hole_cards = PrimeCodedRanks(
    p_table_state->User()->hole_cards(0)->GetOpenHoldemRank(),
    p_table_state->User()->hole_cards(1)->GetOpenHoldemRank());
	_prime_coded_board_cards = PrimeCodedRanks(
    p_table_state->CommonCards(0)->GetOpenHoldemRank(),
    p_table_state->CommonCards(1)->GetOpenHoldemRank(),
    p_table_state->CommonCards(2)->GetOpenHoldemRank(),
    p_table_state->TurnCard()->GetOpenHoldemRank(),
    p_table_state->RiverCard()->GetOpenHoldemRank());
   write_log(preferences.debug_hand_and_baord_expressions(), 
		"[CSymbolEngineOpenPPLHandAndBoardExpression] _prime_coded_hole_cards = %i\n",
		_prime_coded_hole_cards);
	 write_log(preferences.debug_hand_and_baord_expressions(), 
		"[CSymbolEngineOpenPPLHandAndBoardExpression] _prime_coded_board_cards = %i\n",
		_prime_coded_board_cards);
}
bool CSymbolEngineOpenPPLHandAndBoardExpression::EvaluateSymbol(const char *name, double *result, bool log /* = false */) {
	// First check, if hand$ or board$ and/or Suited
	// At the same time remove the unnecessary parts of the expression
	if (memcmp(name, "hand$", 5) == 0) {
		is_hand_expression  = true;
		is_board_expression = false;
		hand_or_board_expression = name;
		write_log(preferences.debug_hand_and_baord_expressions(), 
			"[CSymbolEngineOpenPPLHandAndBoardExpression] Evaluating %s\n",
			hand_or_board_expression);
		hand_or_board_expression = CStringRemoveLeft(hand_or_board_expression, 5);
		prime_coded_available_ranks = _prime_coded_hole_cards;
	}	else if	(memcmp(name, "board$", 6) == 0) {
		is_hand_expression  = false;
		is_board_expression = true;
		hand_or_board_expression = name;
		write_log(preferences.debug_hand_and_baord_expressions(), 
			"[CSymbolEngineOpenPPLHandAndBoardExpression] Evaluating %s\n",
			hand_or_board_expression);
		hand_or_board_expression = CStringRemoveLeft(hand_or_board_expression, 6);
		prime_coded_available_ranks = _prime_coded_board_cards;
	}	else {
		// Quick exit on other symbols
		return false;
	}

  write_log(preferences.debug_hand_and_baord_expressions(), 
    "[CSymbolEngineOpenPPLHandAndBoardExpression] Encoded available ranks> %i\n",
    prime_coded_available_ranks);
	bool is_suited_expression = false;
	assert(is_hand_expression || is_board_expression);

	if (hand_or_board_expression.Right(6).MakeLower() == "suited") {
		write_log(preferences.debug_hand_and_baord_expressions(), 
			"[CSymbolEngineOpenPPLHandAndBoardExpression] Suited expression\n");
		is_suited_expression = true;
		hand_or_board_expression = CStringRemoveRight(
			hand_or_board_expression, 6);
	}
	// Checking ranks with potential multiplicity > 1, not caring about suits.
	// We do this at the very beginning, as this is a very quick test
	// and most real-world-use-cases will be false, so we get a fast exit.
	int prime_coded_search_expression = PrimeCodedRanks(hand_or_board_expression);
  write_log(preferences.debug_hand_and_baord_expressions(), 
    "[CSymbolEngineOpenPPLHandAndBoardExpression] Encoded searched ranks> %i\n",
    prime_coded_available_ranks);
	if ((prime_coded_available_ranks % prime_coded_search_expression) != 0)	{
		// Division without reminder not possible.
		// Therefore different primes in the search-expression
		// Therefore ranks that do not fit available ranks.
		write_log(preferences.debug_hand_and_baord_expressions(), 
			"[CSymbolEngineOpenPPLHandAndBoardExpression] No match, because ranks do not fit\n");
		*result = false;
		return true;
	}

	// This was super-elegant, but unfortunatelly there can be 
	// SUITED expressions, which we can only solve with srankbits.
	// Ranks in the expression (to be searched)
	if (is_suited_expression)	{
		int rankbits_to_be_searched = CardStringToRankbits(((char*)hand_or_board_expression.GetString()));
		write_log(preferences.debug_hand_and_baord_expressions(), 
			"[CSymbolEngineOpenPPLHandAndBoardExpression] rank bits for %s = %i\n",
			hand_or_board_expression.GetString(), rankbits_to_be_searched);
		if (is_hand_expression)	{
			// Suited hand-expression
			// Ranks already checked, there are only 2, this simplifies things
			if (!p_symbol_engine_cards->issuited()) {
        write_log(preferences.debug_hand_and_baord_expressions(), 
			    "[CSymbolEngineOpenPPLHandAndBoardExpression] No match, because off-suited hole-cards\n");
				// No suited ranks available
				*result = false;
				return true;
			}
		}	else {
			// Suited board-expression
			int rankbits_available = p_symbol_engine_pokerval->srankbitscommon();
	
			// Check ranks in expression against available ranks
			if ((rankbits_to_be_searched & rankbits_available) != rankbits_to_be_searched)
			{
				write_log(preferences.debug_hand_and_baord_expressions(),
					"[CSymbolEngineOpenPPLHandAndBoardExpression] No match, because suited rankbits do not fit\n");
				*result = false;
				return true;
			}
		}
	}

	// Third case: cards with individual suits
	int length = hand_or_board_expression.GetLength();
	for (int i=0; i<(length-1); i++) 	{
		if (IsCardRankCharacter(hand_or_board_expression[i])
			  && IsCardSuitCharacter(hand_or_board_expression[i+1])) {
			CString card_with_specific_suit =
				CString(hand_or_board_expression[i])
				+ CString(hand_or_board_expression[i+1]);
			int icard_with_specific_suit = CardStringToCardNumber(
				((char*)card_with_specific_suit.GetString()));

			// Check if card is on the board or in the hand
			if (is_hand_expression) {
				if (!IsCardInCollection(icard_with_specific_suit,
					  p_table_state->User()->_hole_cards[0].GetValue(),
            p_table_state->User()->_hole_cards[1].GetValue())) {
					write_log(preferences.debug_hand_and_baord_expressions(),
						"[CSymbolEngineOpenPPLHandAndBoardExpression] No match, concrete hole cards do not fit\n");
					*result = false;
					return true;
				}
			}	else {
				if (!IsCardInCollection(icard_with_specific_suit,
            p_table_state->_common_cards[0].GetValue(),
            p_table_state->_common_cards[1].GetValue(),
            p_table_state->_common_cards[2].GetValue(),
            p_table_state->_common_cards[3].GetValue(),
            p_table_state->_common_cards[4].GetValue())) 	{
					write_log(preferences.debug_hand_and_baord_expressions(),
						"[CSymbolEngineOpenPPLHandAndBoardExpression] No match, concrete board cards do not fit\n");
					*result = false;
					return true;
				}
			}
		}	
	}
	write_log(preferences.debug_hand_and_baord_expressions(),
		"[CSymbolEngineOpenPPLHandAndBoardExpression] successful match\n");
	*result = true;
	return true;
}