code: package edu.berkeley.nlp.PCFGLA;
  
  import java.io.Serializable;
  import java.util.Random;
  
  import edu.berkeley.nlp.util.ArrayUtil;
  import edu.berkeley.nlp.util.Numberer;
  import edu.berkeley.nlp.util.StringUtils;
  
  /**
   * Binary rules (ints for parent, left and right children)
   * 
   * @author Dan Klein
   */
  
  public class BinaryRule extends Rule implements Serializable,
  		java.lang.Comparable {
  
  	public short leftChildState = -1;
  	public short rightChildState = -1;
  	/**
  	 * NEW: scores[leftSubState][rightSubState][parentSubState] gives score for
  	 * this rule
  	 */
  	public double[][][] scores;
  
  	/**
  	 * Creates a BinaryRule from String s, assuming it was created using
  	 * toString().
  	 * 
  	 * @param s
  	 */
  	/*
  	 * public BinaryRule(String s, Numberer n) { String[] fields =
  	 * StringUtils.splitOnCharWithQuoting(s, ' ', '\"', '\\'); //
  	 * System.out.println("fields:\n" + fields[0] + "\n" + fields[2] + "\n" +
  	 * fields[3] + "\n" + fields[4]); this.parent = n.number(fields[0]);
  	 * this.leftChild = n.number(fields[2]); this.rightChild =
  	 * n.number(fields[3]); this.score = Double.parseDouble(fields[4]); }
  	 */
  	public BinaryRule(short pState, short lState, short rState,
  			double[][][] scores) {
  		this.parentState = pState;
  		this.leftChildState = lState;
  		this.rightChildState = rState;
  		this.scores = scores;
  	}
  
  	public BinaryRule(short pState, short lState, short rState) {
  		this.parentState = pState;
  		this.leftChildState = lState;
  		this.rightChildState = rState;
  		// this.scores = new double[1][1][1];
  	}
  
  	/** Copy constructor */
  	public BinaryRule(BinaryRule b) {
  		this(b.parentState, b.leftChildState, b.rightChildState, ArrayUtil
  				.copy(b.scores));
  	}
  
  	public BinaryRule(BinaryRule b, double[][][] newScores) {
  		this(b.parentState, b.leftChildState, b.rightChildState, newScores);
  	}
  
  	// public BinaryRule(short pState, short lState, short rState, short
  	// pSubStates, int lSubStates, int rSubStates) {
  	// this.parentState = pState;
  	// this.leftChildState = lState;
  	// this.rightChildState = rState;
  	// this.scores = new double[lSubStates][rSubStates][pSubStates];
  	// }
  
  	@Override
  	public int hashCode() {
  		return (parentState << 16) ^ (leftChildState << 8)
  				^ (rightChildState);
  	}
  
  	@Override
  	public boolean equals(Object o) {
  		if (this == o) {
  			return true;
  		}
  		if (o instanceof BinaryRule) {
  			BinaryRule br = (BinaryRule) o;
  			if (parentState == br.parentState
  					&& leftChildState == br.leftChildState
  					&& rightChildState == br.rightChildState) {
  				return true;
  			}
  		}
  		return false;
  	}
  
  	private static final char[] charsToEscape = new char[] { '\"' };
  
  	@Override
  	public String toString() {
  		Numberer n = Numberer.getGlobalNumberer("tags");
  		String lState = (String) n.object(leftChildState);
  		if (lState.endsWith("^g"))
  			lState = lState.substring(0, lState.length() - 2);
  		String rState = (String) n.object(rightChildState);
  		if (rState.endsWith("^g"))
  			rState = rState.substring(0, rState.length() - 2);
  		String pState = (String) n.object(parentState);
  		if (pState.endsWith("^g"))
  			pState = pState.substring(0, pState.length() - 2);
  		StringBuilder sb = new StringBuilder();
  		if (scores == null)
  			return pState + " -> " + lState + " " + rState + "\n";
  		// sb.append(pState+ " -> "+lState+ " "+rState+ "\n");
  		for (int lS = 0; lS < scores.length; lS++) {
  			for (int rS = 0; rS < scores[lS].length; rS++) {
  				if (scores[lS][rS] == null)
  					continue;
  				for (int pS = 0; pS < scores[lS][rS].length; pS++) {
  					double p = scores[lS][rS][pS];
  					if (p > 0)
  						sb.append(pState + "_" + pS + " -> " + lState + "_"
  								+ lS + " " + rState + "_" + rS + " " + p + "\n");
  				}
  			}
  		}
  		return sb.toString();
  	}
  
  	public String toString_old() {
  		Numberer n = Numberer.getGlobalNumberer("tags");
  		return "\""
  				+ StringUtils.escapeString(n.object(parentState).toString(),
  						charsToEscape, '\\')
  				+ "\" -> \""
  				+ StringUtils.escapeString(n.object(leftChildState).toString(),
  						charsToEscape, '\\')
  				+ "\" \""
  				+ StringUtils.escapeString(
  						n.object(rightChildState).toString(), charsToEscape,
  						'\\') + "\" " + ArrayUtil.toString(scores);
  	}
  
  	public int compareTo(Object o) {
  		BinaryRule ur = (BinaryRule) o;
  		if (parentState < ur.parentState) {
  			return -1;
  		}
  		if (parentState > ur.parentState) {
  			return 1;
  		}
  		if (leftChildState < ur.leftChildState) {
  			return -1;
  		}
  		if (leftChildState > ur.leftChildState) {
  			return 1;
  		}
  		if (rightChildState < ur.rightChildState) {
  			return -1;
  		}
  		if (rightChildState > ur.rightChildState) {
  			return 1;
  		}
  		return 0;
  	}
  
  	public short getLeftChildState() {
  		return leftChildState;
  	}
  
  	public short getRightChildState() {
  		return rightChildState;
  	}
  
  	// public void setScore(int pS, int lS, int rS, double score){
  	// // sets the score for a particular combination of substates
  	// scores[lS][rS][pS] = score;
  	// }
  
  	public double getScore(int pS, int lS, int rS) {
  		// gets the score for a particular combination of substates
  		if (scores[lS][rS] == null) {
  			if (logarithmMode)
  				return Double.NEGATIVE_INFINITY;
  			return 0;
  		}
  		return scores[lS][rS][pS];
  	}
  
  	public void setScores2(double[][][] scores) {
  		this.scores = scores;
  	}
  
  	/**
  	 * scores[parentSubState][leftSubState][rightSubState] gives score for this
  	 * rule
  	 */
  	public double[][][] getScores2() {
  		return scores;
  	}
  
  	public void setNodes(short pState, short lState, short rState) {
  		this.parentState = pState;
  		this.leftChildState = lState;
  		this.rightChildState = rState;
  	}
  
  	private static final long serialVersionUID = 2L;
  
  	public BinaryRule splitRule(short[] numSubStates, short[] newNumSubStates,
  			Random random, double randomness, boolean doNotNormalize, int mode) {
  		// when splitting on parent, never split on ROOT
  		int parentSplitFactor = this.getParentState() == 0 ? 1 : 2; // should
  
  		if (newNumSubStates[this.parentState] == numSubStates[this.parentState]) {
  			parentSplitFactor = 1;
  		}
  		int lChildSplitFactor = 2;
  		if (newNumSubStates[this.leftChildState] == numSubStates[this.leftChildState]) {
  			lChildSplitFactor = 1;
  		}
  		int rChildSplitFactor = 2;
  		if (newNumSubStates[this.rightChildState] == numSubStates[this.rightChildState]) {
  			rChildSplitFactor = 1;
  		}
  
  		double[][][] oldScores = this.getScores2();
  		double[][][] newScores = new double[oldScores.length
  				* lChildSplitFactor][oldScores[0].length * rChildSplitFactor][];
  		// [oldScores[0][0].length * parentSplitFactor];
  		// Arrays.fill(newScores,Double.NEGATIVE_INFINITY);
  		// for all current substates
  		for (short lcS = 0; lcS < oldScores.length; lcS++) {
  			for (short rcS = 0; rcS < oldScores[0].length; rcS++) {
  				if (oldScores[lcS][rcS] == null)
  					continue;
  
  				for (short lc = 0; lc < lChildSplitFactor; lc++) {
  					for (short rc = 0; rc < rChildSplitFactor; rc++) {
  						short newLCS = (short) (lChildSplitFactor * lcS + lc);
  						short newRCS = (short) (rChildSplitFactor * rcS + rc);
  						newScores[newLCS][newRCS] = new double[newNumSubStates[this.parentState]];
  					}
  				}
  
  				for (short pS = 0; pS < oldScores[lcS][rcS].length; pS++) {
  					double score = oldScores[lcS][rcS][pS];
  					// split on parent
  					for (short p = 0; p < parentSplitFactor; p++) {
  						double divFactor = (doNotNormalize) ? 1.0
  								: lChildSplitFactor * rChildSplitFactor;
  						double randomComponentLC = score / divFactor
  								* randomness / 100
  								* (random.nextDouble() - 0.5);
  						// split on left child
  						for (short lc = 0; lc < lChildSplitFactor; lc++) {
  							// reverse the random component for half of the
  							// rules
  							if (lc == 1) {
  								randomComponentLC *= -1;
  							}
  							// don't add randomness if we're not splitting
  							if (lChildSplitFactor == 1) {
  								randomComponentLC = 0;
  							}
  							double randomComponentRC = score / divFactor
  									* randomness / 100
  									* (random.nextDouble() - 0.5);
  							// split on right child
  							for (short rc = 0; rc < rChildSplitFactor; rc++) {
  								// reverse the random component for half of the
  								// rules
  								if (rc == 1) {
  									randomComponentRC *= -1;
  								}
  								// don't add randomness if we're not splitting
  								if (rChildSplitFactor == 1) {
  									randomComponentRC = 0;
  								}
  								// set new score; divide score by 4 because
  								// we're dividing each
  								// binary rule under a parent into 4
  								short newPS = (short) (parentSplitFactor * pS + p);
  								short newLCS = (short) (lChildSplitFactor * lcS + lc);
  								short newRCS = (short) (rChildSplitFactor * rcS + rc);
  								double splitFactor = (doNotNormalize) ? 1.0
  										: lChildSplitFactor * rChildSplitFactor;
  								newScores[newLCS][newRCS][newPS] = (score
  										/ (splitFactor) + randomComponentLC + randomComponentRC);
  								// sparsifier
  								// .splitBinaryWeight(oldRule.getParentState(),
  								// pS,
  								// oldRule.getLeftChildState(), lcS, oldRule
  								// .getRightChildState(), rcS, newPS, newLCS,
  								// newRCS, lChildSplitFactor, rChildSplitFactor,
  								// randomComponentLC, randomComponentRC, ,
  								// tagNumberer);
  								if (mode == 2)
  									newScores[newLCS][newRCS][newPS] = 1.0 + random
  											.nextDouble() / 100.0;
  							}
  						}
  					}
  				}
  			}
  		}
  		BinaryRule newRule = new BinaryRule(this, newScores);
  		return newRule;
  
  	}
  
  }
  