// Path: src/main/java/com/asher_stern/crf/utilities/CrfException.java
// public class CrfException extends RuntimeException
// {
// 	private static final long serialVersionUID = 5414394233000815286L;
// 
// 	public CrfException()
// 	{
// 		super();
// 	}
// 
// 	public CrfException(String message, Throwable cause,
// 			boolean enableSuppression, boolean writableStackTrace)
// 	{
// 		super(message, cause, enableSuppression, writableStackTrace);
// 	}
// 
// 	public CrfException(String message, Throwable cause)
// 	{
// 		super(message, cause);
// 	}
// 
// 	public CrfException(String message)
// 	{
// 		super(message);
// 	}
// 
// 	public CrfException(Throwable cause)
// 	{
// 		super(cause);
// 	}
// 	
// 
// }
// 
// Path: src/main/java/com/asher_stern/crf/utilities/ArithmeticUtilities.java
// public class ArithmeticUtilities
// {
// 	public static final MathContext MC = MathContext.DECIMAL128;
// 	
// 	public static final BigDecimal DOUBLE_MAX = big(Double.MAX_VALUE);
// 	
// 	public static final BigDecimal BIG_DECIMAL_E = big(Math.E);
// 	public static final BigDecimal BIG_DECIMAL_TWO = new BigDecimal("2.0", MC);
// 	public static final BigDecimal BIG_DECIMAL_E_TO_512 = BIG_DECIMAL_E.pow(512);
// 	public static final BigDecimal BIG_DECIMAL_512 = new BigDecimal("512", MC);
// 	public static final BigDecimal BIG_DECIMAL_LOG_DOUBLE_MAX = big(Math.log(Double.MAX_VALUE));
// 	
// 	public static BigDecimal big(double d)
// 	{
// 		return new BigDecimal(d, MC);
// 	}
// 	
// 
// 	public static BigDecimal log(BigDecimal d)
// 	{
// 		// long debug_counter = 0;
// 		BigDecimal ret = BigDecimal.ZERO;
// 		while (d.compareTo(DOUBLE_MAX)>0)
// 		{
// 			ret = safeAdd(ret, BIG_DECIMAL_512);
// 			d = safeDivide(d, BIG_DECIMAL_E_TO_512);
// 			if (d.compareTo(BigDecimal.ONE)<0) {throw new CrfException("Anomaly");}
// 			// ++debug_counter;
// 		}
// 		ret = safeAdd(ret, big(Math.log(d.doubleValue())));
// 		// if ( logger.isDebugEnabled() && (debug_counter>0) ) {logger.debug("log() performed "+debug_counter+" iterations.");}
// 		return ret;
// 	}
// 	
// 	
// 	public static BigDecimal exp(BigDecimal d)
// 	{
// 		if (d.compareTo(BIG_DECIMAL_LOG_DOUBLE_MAX)<=0)
// 		{
// 			return big(Math.exp(d.doubleValue()));
// 		}
// 		else
// 		{
// 			BigDecimal half = safeDivide(d, BIG_DECIMAL_TWO);
// 			BigDecimal halfExp = exp(half);
// 			return safeMultiply(halfExp, halfExp);
// 		}
// 	}
// 	
// 	public static BigDecimal safeAdd(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.add(d2, MC);
// 	}
// 	
// 	public static BigDecimal safeSubtract(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.subtract(d2, MC);
// 	}
// 	
// 	public static BigDecimal safeMultiply(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.multiply(d2, MC);
// 	}
// 	
// 	public static BigDecimal safeMultiply(final BigDecimal d1, final BigDecimal d2, BigDecimal...ds)
// 	{
// 		BigDecimal ret = safeMultiply(d1, d2);
// 		for (BigDecimal d : ds)
// 		{
// 			ret = safeMultiply(ret, d);
// 		}
// 		return ret;
// 	}
// 	
// 	public static BigDecimal safeDivide(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.divide(d2, MC);
// 	}
// 	
// 	@SuppressWarnings("unused")
// 	private static final Logger logger = Logger.getLogger(ArithmeticUtilities.class);
// }


import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.asher_stern.crf.utilities.CrfException;
import static com.asher_stern.crf.utilities.ArithmeticUtilities.*;
					if (index>0)
					{
						valueByPrevious = safeMultiply(valueByPrevious, delta_viterbiForward[index-1].get(tagOfPrevious));
					}

					boolean maxSoFarDetected = false;
					if (null==maxValueByPrevious) {maxSoFarDetected=true;}
					else if (maxValueByPrevious.compareTo(valueByPrevious) < 0) {maxSoFarDetected=true;}
					
					if (maxSoFarDetected)
					{
						maxValueByPrevious=valueByPrevious;
						tagOfPreviousWithMaxValue=tagOfPrevious;
					}
				} // end for-each previous-tag
				argmaxTags[index].put(tag,tagOfPreviousWithMaxValue); // i.e. If the tag for token number "index" is "tag", then the tag for token "index-1" is "tagOfPreviousWithMaxValue". 
				delta_viterbiForwardCurrentToken.put(tag,maxValueByPrevious);
			} // end for-each current-tag
			delta_viterbiForward[index]=delta_viterbiForwardCurrentToken;
		} // end for-each token-in-sentence

		G tagOfLastToken = getArgMax(delta_viterbiForward[sentence.length-1]);
		
		result = (G[]) Array.newInstance(tagOfLastToken.getClass(), sentence.length); // new G[sentence.length];
		G bestTagCurrentIndex = tagOfLastToken;
		for (int tokenIndex=sentence.length-1;tokenIndex>=0;--tokenIndex)
		{
			result[tokenIndex] = bestTagCurrentIndex;
			bestTagCurrentIndex = argmaxTags[tokenIndex].get(bestTagCurrentIndex);
		}
// Path: src/main/java/com/asher_stern/crf/utilities/CrfException.java
// public class CrfException extends RuntimeException
// {
// 	private static final long serialVersionUID = 5414394233000815286L;
// 
// 	public CrfException()
// 	{
// 		super();
// 	}
// 
// 	public CrfException(String message, Throwable cause,
// 			boolean enableSuppression, boolean writableStackTrace)
// 	{
// 		super(message, cause, enableSuppression, writableStackTrace);
// 	}
// 
// 	public CrfException(String message, Throwable cause)
// 	{
// 		super(message, cause);
// 	}
// 
// 	public CrfException(String message)
// 	{
// 		super(message);
// 	}
// 
// 	public CrfException(Throwable cause)
// 	{
// 		super(cause);
// 	}
// 	
// 
// }
// 
// Path: src/main/java/com/asher_stern/crf/utilities/ArithmeticUtilities.java
// public class ArithmeticUtilities
// {
// 	public static final MathContext MC = MathContext.DECIMAL128;
// 	
// 	public static final BigDecimal DOUBLE_MAX = big(Double.MAX_VALUE);
// 	
// 	public static final BigDecimal BIG_DECIMAL_E = big(Math.E);
// 	public static final BigDecimal BIG_DECIMAL_TWO = new BigDecimal("2.0", MC);
// 	public static final BigDecimal BIG_DECIMAL_E_TO_512 = BIG_DECIMAL_E.pow(512);
// 	public static final BigDecimal BIG_DECIMAL_512 = new BigDecimal("512", MC);
// 	public static final BigDecimal BIG_DECIMAL_LOG_DOUBLE_MAX = big(Math.log(Double.MAX_VALUE));
// 	
// 	public static BigDecimal big(double d)
// 	{
// 		return new BigDecimal(d, MC);
// 	}
// 	
// 
// 	public static BigDecimal log(BigDecimal d)
// 	{
// 		// long debug_counter = 0;
// 		BigDecimal ret = BigDecimal.ZERO;
// 		while (d.compareTo(DOUBLE_MAX)>0)
// 		{
// 			ret = safeAdd(ret, BIG_DECIMAL_512);
// 			d = safeDivide(d, BIG_DECIMAL_E_TO_512);
// 			if (d.compareTo(BigDecimal.ONE)<0) {throw new CrfException("Anomaly");}
// 			// ++debug_counter;
// 		}
// 		ret = safeAdd(ret, big(Math.log(d.doubleValue())));
// 		// if ( logger.isDebugEnabled() && (debug_counter>0) ) {logger.debug("log() performed "+debug_counter+" iterations.");}
// 		return ret;
// 	}
// 	
// 	
// 	public static BigDecimal exp(BigDecimal d)
// 	{
// 		if (d.compareTo(BIG_DECIMAL_LOG_DOUBLE_MAX)<=0)
// 		{
// 			return big(Math.exp(d.doubleValue()));
// 		}
// 		else
// 		{
// 			BigDecimal half = safeDivide(d, BIG_DECIMAL_TWO);
// 			BigDecimal halfExp = exp(half);
// 			return safeMultiply(halfExp, halfExp);
// 		}
// 	}
// 	
// 	public static BigDecimal safeAdd(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.add(d2, MC);
// 	}
// 	
// 	public static BigDecimal safeSubtract(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.subtract(d2, MC);
// 	}
// 	
// 	public static BigDecimal safeMultiply(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.multiply(d2, MC);
// 	}
// 	
// 	public static BigDecimal safeMultiply(final BigDecimal d1, final BigDecimal d2, BigDecimal...ds)
// 	{
// 		BigDecimal ret = safeMultiply(d1, d2);
// 		for (BigDecimal d : ds)
// 		{
// 			ret = safeMultiply(ret, d);
// 		}
// 		return ret;
// 	}
// 	
// 	public static BigDecimal safeDivide(final BigDecimal d1, final BigDecimal d2)
// 	{
// 		return d1.divide(d2, MC);
// 	}
// 	
// 	@SuppressWarnings("unused")
// 	private static final Logger logger = Logger.getLogger(ArithmeticUtilities.class);
// }


// Path: src/main/java/com/asher_stern/crf/crf/CrfInferenceViterbi.java
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.asher_stern.crf.utilities.CrfException;
import static com.asher_stern.crf.utilities.ArithmeticUtilities.*;

					if (index>0)
					{
						valueByPrevious = safeMultiply(valueByPrevious, delta_viterbiForward[index-1].get(tagOfPrevious));
					}

					boolean maxSoFarDetected = false;
					if (null==maxValueByPrevious) {maxSoFarDetected=true;}
					else if (maxValueByPrevious.compareTo(valueByPrevious) < 0) {maxSoFarDetected=true;}
					
					if (maxSoFarDetected)
					{
						maxValueByPrevious=valueByPrevious;
						tagOfPreviousWithMaxValue=tagOfPrevious;
					}
				} // end for-each previous-tag
				argmaxTags[index].put(tag,tagOfPreviousWithMaxValue); // i.e. If the tag for token number "index" is "tag", then the tag for token "index-1" is "tagOfPreviousWithMaxValue". 
				delta_viterbiForwardCurrentToken.put(tag,maxValueByPrevious);
			} // end for-each current-tag
			delta_viterbiForward[index]=delta_viterbiForwardCurrentToken;
		} // end for-each token-in-sentence

		G tagOfLastToken = getArgMax(delta_viterbiForward[sentence.length-1]);
		
		result = (G[]) Array.newInstance(tagOfLastToken.getClass(), sentence.length); // new G[sentence.length];
		G bestTagCurrentIndex = tagOfLastToken;
		for (int tokenIndex=sentence.length-1;tokenIndex>=0;--tokenIndex)
		{
			result[tokenIndex] = bestTagCurrentIndex;
			bestTagCurrentIndex = argmaxTags[tokenIndex].get(bestTagCurrentIndex);
		}
		if (bestTagCurrentIndex!=null) {throw new CrfException("BUG");} // the tag of "before the first token" must be null.