Commits

petermr  committed 2e06956

added more elements and more tests

  • Participants
  • Parent commits 6b6f5c1

Comments (0)

Files changed (23)

File src/main/java/org/xmlcml/mathml/AbstractMathElement.java

 	protected void assertStringContent() {
 		value = this.getValue();
 		if (value == null || value.trim().length() == 0) {
-			throw new RuntimeException("Expected string content");
+			throw new RuntimeException("Expected string content in: "+this);
 		}
 	}
 
 		}
 	}
 	
+	public void debug(String message) {
+		CMLUtil.debug(this, message);
+	}
+	public void tidy() {
+		boolean change = false;
+		while (true) {
+			change = false;
+			change |= TIMESElement.tidyZero(this);
+			change |= TIMESElement.tidyOne(this);
+			change |= PLUSElement.tidyZero(this);
+			change |= MINUSElement.tidyZero(this);
+			change |= POWERElement.tidyZero(this);
+			change |= POWERElement.tidyOne(this);
+			if (!change) break;
+		}
+	}
 }

File src/main/java/org/xmlcml/mathml/BVARElement.java

 	
 	@Override
 	protected void validate() {
-		assertNoElementChildren();
-		assertStringContent();
+		assertElementChildren(1,2);
+		assertNoStringContent();
 	}
 
 	public Object eval() {

File src/main/java/org/xmlcml/mathml/CONDITIONElement.java

+package org.xmlcml.mathml;
+
+import java.util.List;
+
+import nu.xom.Elements;
+
+public class CONDITIONElement extends AbstractMathElement  {
+
+	public static String TAG = "condition";
+	
+	public CONDITIONElement() {
+		super(TAG);
+	}
+	public CONDITIONElement(String xml) {
+		super(xml);
+	}
+	public CONDITIONElement(AbstractMathElement elem) {
+		this();
+		throw new RuntimeException("NYI");
+	}
+	
+	@Override
+	protected void validate() {
+		assertElementChildren(1, 99);
+		assertNoStringContent();
+	}
+
+	public Object eval() {
+		throw new RuntimeException("NYI");
+	}
+
+	public Object apply(Elements elements) {
+//		if (elements.size() == 2) {
+//			Object o1 = ((AbstractMathElement)elements.get(1)).eval();
+//			if (o1 instanceof Double) {
+//				return Math.cos((Double) o1);
+//			} else if (o1 instanceof Integer) {
+//				return Math.cos(new Double((Integer) o1));
+//			} else {
+//				return "(cos("+o1.toString()+"))";
+//			}
+//		} else {
+//			throw new RuntimeException("<cos/> takes one arguments");
+//		}
+		return null;
+	}
+	
+	public AbstractMathElement differentiate() {
+		List<AbstractMathElement> following = getFollowingElements(1);
+		APPLYElement result = new APPLYElement(
+			new TIMESElement(),
+			((Differentiable)following.get(0)).differentiate(),
+			new APPLYElement(new SINElement(), AbstractMathElement.copyElement(following.get(0)))
+			);
+		AbstractMathElement result1 = new APPLYElement(
+			new MINUSElement(),
+			result
+			);
+		return result;
+	}
+	
+}

File src/main/java/org/xmlcml/mathml/COSElement.java

 			((Differentiable)following.get(0)).differentiate(),
 			new APPLYElement(new SINElement(), AbstractMathElement.copyElement(following.get(0)))
 			);
-		return new APPLYElement(
+		AbstractMathElement result1 = new APPLYElement(
 			new MINUSElement(),
 			result
 			);
+		return result;
 	}
 	
 }

File src/main/java/org/xmlcml/mathml/LOWLIMITElement.java

+package org.xmlcml.mathml;
+
+import java.util.List;
+
+import nu.xom.Elements;
+
+public class LOWLIMITElement extends AbstractMathElement  {
+
+	public static String TAG = "condition";
+	
+	public LOWLIMITElement() {
+		super(TAG);
+	}
+	public LOWLIMITElement(String xml) {
+		super(xml);
+	}
+	public LOWLIMITElement(AbstractMathElement elem) {
+		this();
+		throw new RuntimeException("NYI");
+	}
+	
+	@Override
+	protected void validate() {
+		assertElementChildren(1, 99);
+		assertNoStringContent();
+	}
+
+	public Object eval() {
+		throw new RuntimeException("NYI");
+	}
+
+	public Object apply(Elements elements) {
+//		if (elements.size() == 2) {
+//			Object o1 = ((AbstractMathElement)elements.get(1)).eval();
+//			if (o1 instanceof Double) {
+//				return Math.cos((Double) o1);
+//			} else if (o1 instanceof Integer) {
+//				return Math.cos(new Double((Integer) o1));
+//			} else {
+//				return "(cos("+o1.toString()+"))";
+//			}
+//		} else {
+//			throw new RuntimeException("<cos/> takes one arguments");
+//		}
+		return null;
+	}
+	
+	public AbstractMathElement differentiate() {
+		List<AbstractMathElement> following = getFollowingElements(1);
+		APPLYElement result = new APPLYElement(
+			new TIMESElement(),
+			((Differentiable)following.get(0)).differentiate(),
+			new APPLYElement(new SINElement(), AbstractMathElement.copyElement(following.get(0)))
+			);
+		AbstractMathElement result1 = new APPLYElement(
+			new MINUSElement(),
+			result
+			);
+		return result;
+	}
+	
+}

File src/main/java/org/xmlcml/mathml/MINUSElement.java

 import java.util.List;
 
 import nu.xom.Elements;
+import nu.xom.Node;
+import nu.xom.Nodes;
+import nu.xom.ParentNode;
 
 
 public class MINUSElement extends AbstractMathElement implements Operator, Differentiable {
 		return null;
 	}
 	
+	/**
+	 * change 
+	 * <m:apply>
+	 *   <m:plus/>
+	 *   <m:foo/>
+	 *   <m:cn>0</m:cn>
+	 * </m:apply>
+	 * 
+	 * to
+	 * <m:foo/>
+	 * 
+	 * @param element
+	 * @return
+	 */
+	public static boolean tidyZero(AbstractMathElement element) {
+		boolean change = false;
+		Nodes nodes = element.query("descendant-or-self::m:apply[m:minus and m:cn[.='0']]", MATHElement.XPATH_CONTEXT);
+		for (int i = 0; i < nodes.size(); i++) {
+			APPLYElement applyElement = (APPLYElement) nodes.get(i);
+			Node zeroNode = applyElement.query("m:cn [.='0']", MATHElement.XPATH_CONTEXT).get(0);
+			int idx = applyElement.indexOf(zeroNode);
+			if (idx == 1) {
+				zeroNode.detach();
+				change = true;
+			} else {
+				ParentNode parent = applyElement.getParent();
+				if (parent != null) {
+					zeroNode.detach(); // 
+					applyElement.getChildElements().get(0).detach(); // remove minus
+					Node nonZeroNode = applyElement.getChildElements().get(0);
+					nonZeroNode.detach();
+					parent.replaceChild(applyElement, nonZeroNode);
+					change = true;
+				}
+			}
+		}
+		return change;
+	}
+
 }

File src/main/java/org/xmlcml/mathml/PLUSElement.java

 import java.util.List;
 
 import nu.xom.Elements;
+import nu.xom.Node;
+import nu.xom.Nodes;
+import nu.xom.ParentNode;
 
 public class PLUSElement extends AbstractMathElement implements Operator, Differentiable {
 
 		return result;
 	}
 	
+	/**
+	 * change 
+	 * <m:apply>
+	 *   <m:plus/>
+	 *   <m:foo/>
+	 *   <m:cn>0</m:cn>
+	 * </m:apply>
+	 * 
+	 * to
+	 * <m:foo/>
+	 * 
+	 * @param element
+	 * @return
+	 */
+	public static boolean tidyZero(AbstractMathElement element) {
+		boolean change = false;
+		Nodes nodes = element.query("descendant-or-self::m:apply[m:plus and m:cn[.='0']]", MATHElement.XPATH_CONTEXT);
+		for (int i = 0; i < nodes.size(); i++) {
+			APPLYElement applyElement = (APPLYElement) nodes.get(i);
+			ParentNode parent = applyElement.getParent();
+			if (parent != null) {
+				Node zeroNode = applyElement.query("m:cn [.='0']", MATHElement.XPATH_CONTEXT).get(0);
+				zeroNode.detach(); // 
+				applyElement.getChildElements().get(0).detach(); // remove plus
+				Node nonZeroNode = applyElement.getChildElements().get(0);
+				nonZeroNode.detach();
+				parent.replaceChild(applyElement, nonZeroNode);
+				change = true;
+			}
+		}
+		return change;
+	}
+
 }

File src/main/java/org/xmlcml/mathml/POWERElement.java

 import java.util.List;
 
 import nu.xom.Elements;
+import nu.xom.Node;
+import nu.xom.Nodes;
+import nu.xom.ParentNode;
 
 public class POWERElement extends AbstractMathElement implements Operator, Differentiable {
 
 		return result;
 	}
 	
+	/**
+	 * change 
+	 * <m:apply>
+	 *   <m:power/>
+	 *   <m:foo/>
+	 *   <m:cn>0</m:cn>
+	 * </m:apply>
+	 * 
+	 * to
+	 * <m:cn>1</m:cn>
+	 * 
+	 * @param element
+	 * @return
+	 */
+	public static boolean tidyZero(AbstractMathElement element) {
+		boolean change = false;
+		Nodes nodes = element.query("descendant-or-self::m:apply[m:power and m:cn[.='0']]", MATHElement.XPATH_CONTEXT);
+		for (int i = 0; i < nodes.size(); i++) {
+			APPLYElement applyElement = (APPLYElement) nodes.get(i);
+			ParentNode parent = applyElement.getParent();
+			if (parent != null) {
+				Node zero = applyElement.query("m:cn[.='0']", MATHElement.XPATH_CONTEXT).get(0);
+				int idx = applyElement.indexOf(zero);
+				if (idx == 1) {
+					parent.replaceChild(applyElement, new CNElement(0));
+				} else {
+					parent.replaceChild(applyElement, new CNElement(1));
+				}
+				change = true;
+			}
+		}
+		return change;
+	}
+
+	/**
+	 * change 
+	 * <m:apply>
+	 *   <m:times/>
+	 *   <m:foo/>
+	 *   <m:cn>1</m:cn>
+	 * </m:apply>
+	 * 
+	 * to
+	 * <m:cn>0</m:cn>
+	 * 
+	 * @param element
+	 * @return
+	 */
+	public static boolean tidyOne(AbstractMathElement element) {
+		boolean change = false;
+		Nodes nodes = element.query("descendant-or-self::m:apply[m:power and m:cn[.='1']]", MATHElement.XPATH_CONTEXT);
+		for (int i = 0; i < nodes.size(); i++) {
+			APPLYElement applyElement = (APPLYElement) nodes.get(i);
+			ParentNode parent = applyElement.getParent();
+			if (parent != null) {
+				Node oneNode = applyElement.query("m:cn [.='1']", MATHElement.XPATH_CONTEXT).get(0);
+				int idx = parent.indexOf(oneNode);
+				if (idx == 1) {
+					parent.replaceChild(applyElement, new CNElement(1));
+				} else {
+					Node otherNode = applyElement.getChildElements().get(1);
+					otherNode.detach(); // 
+					parent.replaceChild(applyElement, otherNode);
+				}
+				change = true;
+			}
+		}
+		return change;
+	}
+
 
 }

File src/main/java/org/xmlcml/mathml/SUMElement.java

+package org.xmlcml.mathml;
+
+import java.util.List;
+
+import nu.xom.Elements;
+
+public class SUMElement extends AbstractMathElement implements Operator {
+
+	public static String TAG = "sum";
+	private BVARElement bvar;
+	private APPLYElement apply;
+	
+	public SUMElement() {
+		super(TAG);
+	}
+	public SUMElement(String xml) {
+		super(xml);
+	}
+	public SUMElement(AbstractMathElement elem) {
+		this();
+		throw new RuntimeException("NYI");
+	}
+	
+	@Override
+	protected void validate() {
+		assertNoElementChildren();
+		assertNoStringContent();
+	}
+
+	public Object eval() {
+		throw new RuntimeException("NYI");
+	}
+
+	/**
+	 <apply xmlns:m='http://www.w3.org/1998/Math/MathML'>
+	   <sum/>
+	   <bvar>
+	     <ci> x </ci>
+	   </bvar>
+	   <lowlimit>
+	     <ci> a </ci>
+	   </lowlimit>
+	   <uplimit>
+	     <ci> b </ci>
+	   </uplimit>
+	   <apply>
+	     <ci type='fn'> f </ci>
+	     <ci> x </ci>
+	   </apply>
+	 </apply> 
+	    
+		<apply xmlns:m='http://www.w3.org/1998/Math/MathML'>
+	      <sum/>
+	      <bvar>
+	        <ci> x </ci>
+	      </bvar>
+	      <condition>
+	        <apply>
+	          <in/>
+	          <ci> x </ci>
+	          <ci type='set'> B </ci>
+	        </apply>
+	      </condition>
+	      <apply>
+	        <ci type='fn'> f </ci>
+	        <ci> x </ci>
+	      </apply>
+	    </apply> 
+	 * 
+	 */
+	public Object apply(Elements elements) {
+		if (elements.size() < 2) {
+			throw new RuntimeException("not enough siblings of <sum/>");
+		}
+		if (!(elements.get(0) instanceof BVARElement)) {
+			throw new RuntimeException("first element must be <bvar/>");
+		}
+		bvar = (BVARElement) elements.get(0);
+		if (!(elements.get(elements.size()-1) instanceof APPLYElement)) {
+			throw new RuntimeException("last element must be <apply/>");
+		}
+		apply = (APPLYElement) elements.get(0);
+		for (int i = 1; i < elements.size()-1; i++) {
+			if (elements.get(i) instanceof CONDITIONElement) {
+				CONDITIONElement condition = (CONDITIONElement) elements.get(i);
+			}
+		}
+		return null;
+	}
+	
+	public AbstractMathElement differentiate() {
+		throw new RuntimeException("NYI");
+	}
+	
+}

File src/main/java/org/xmlcml/mathml/TIMESElement.java

 import java.util.List;
 
 import nu.xom.Elements;
-
-import org.xmlcml.cml.base.CMLUtil;
+import nu.xom.Node;
+import nu.xom.Nodes;
+import nu.xom.ParentNode;
 
 public class TIMESElement extends AbstractMathElement implements Operator, Differentiable {
 
 				d0Result);
 		return new APPLYElement(new PLUSElement(), term0, term1);
 	}
+	
+	/**
+	 * change 
+	 * <m:apply>
+	 *   <m:times/>
+	 *   <m:foo/>
+	 *   <m:cn>0</m:cn>
+	 * </m:apply>
+	 * 
+	 * to
+	 * <m:cn>0</m:cn>
+	 * 
+	 * @param element
+	 * @return
+	 */
+	public static boolean tidyZero(AbstractMathElement element) {
+		boolean change = false;
+		Nodes nodes = element.query("descendant-or-self::m:apply[m:times and m:cn[.='0']]", MATHElement.XPATH_CONTEXT);
+		for (int i = 0; i < nodes.size(); i++) {
+			APPLYElement applyElement = (APPLYElement) nodes.get(i);
+			ParentNode parent = applyElement.getParent();
+			if (parent != null) {
+				parent.replaceChild(applyElement, new CNElement(0));
+				change = true;
+			}
+		}
+		return change;
+	}
+
+	/**
+	 * change 
+	 * <m:apply>
+	 *   <m:times/>
+	 *   <m:foo/>
+	 *   <m:cn>1</m:cn>
+	 * </m:apply>
+	 * 
+	 * to
+	 * <m:cn>0</m:cn>
+	 * 
+	 * @param element
+	 * @return
+	 */
+	public static boolean tidyOne(AbstractMathElement element) {
+		boolean change = false;
+		Nodes nodes = element.query(".//m:apply[m:times and m:cn[.='1']]", MATHElement.XPATH_CONTEXT);
+		for (int i = 0; i < nodes.size(); i++) {
+			APPLYElement applyElement = (APPLYElement) nodes.get(i);
+			Node oneNode = applyElement.query("m:cn [.='1']", MATHElement.XPATH_CONTEXT).get(0);
+			oneNode.detach(); // 
+			applyElement.getChildElements().get(0).detach(); // remove times
+			Node otherNode = applyElement.getChildElements().get(0);
+			otherNode.detach();
+			applyElement.getParent().replaceChild(applyElement, otherNode);
+			change = true;
+		}
+		return change;
+	}
 
 }

File src/main/java/org/xmlcml/mathml/Tidyable.java

+package org.xmlcml.mathml;
+
+public interface Tidyable {
+	void tidy();
+}

File src/main/java/org/xmlcml/mathml/UPLIMITElement.java

+package org.xmlcml.mathml;
+
+import java.util.List;
+
+import nu.xom.Elements;
+
+public class UPLIMITElement extends AbstractMathElement  {
+
+	public static String TAG = "condition";
+	
+	public UPLIMITElement() {
+		super(TAG);
+	}
+	public UPLIMITElement(String xml) {
+		super(xml);
+	}
+	public UPLIMITElement(AbstractMathElement elem) {
+		this();
+		throw new RuntimeException("NYI");
+	}
+	
+	@Override
+	protected void validate() {
+		assertElementChildren(1, 99);
+		assertNoStringContent();
+	}
+
+	public Object eval() {
+		throw new RuntimeException("NYI");
+	}
+
+	public Object apply(Elements elements) {
+//		if (elements.size() == 2) {
+//			Object o1 = ((AbstractMathElement)elements.get(1)).eval();
+//			if (o1 instanceof Double) {
+//				return Math.cos((Double) o1);
+//			} else if (o1 instanceof Integer) {
+//				return Math.cos(new Double((Integer) o1));
+//			} else {
+//				return "(cos("+o1.toString()+"))";
+//			}
+//		} else {
+//			throw new RuntimeException("<cos/> takes one arguments");
+//		}
+		return null;
+	}
+	
+	public AbstractMathElement differentiate() {
+		List<AbstractMathElement> following = getFollowingElements(1);
+		APPLYElement result = new APPLYElement(
+			new TIMESElement(),
+			((Differentiable)following.get(0)).differentiate(),
+			new APPLYElement(new SINElement(), AbstractMathElement.copyElement(following.get(0)))
+			);
+		AbstractMathElement result1 = new APPLYElement(
+			new MINUSElement(),
+			result
+			);
+		return result;
+	}
+	
+}

File src/test/java/org/xmlcml/mathml/APPLYElementTest.java

 		String ss = "" +
 		"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>"+
 		"  <m:lambda>"+
-		"    <m:bvar>R</m:bvar>"+
+		"    <m:bvar><ci>R</ci></m:bvar>"+
 		"    <m:apply>"+
 		"      <m:minus/>"+
 		"      <m:apply>"+
 	public void testDiffCompleteContent() {
 		String ss = "" +
 		"<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-		"  <m:bvar>R</m:bvar>" +
+		"  <m:bvar><ci>R</ci></m:bvar>" +
 		"  <m:apply>"+
 		"    <m:minus/>"+
 		"    <m:apply>"+

File src/test/java/org/xmlcml/mathml/BVARElementTest.java

+package org.xmlcml.mathml;
+
+import nu.xom.Element;
+import nu.xom.Node;
+import nu.xom.Text;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.xmlcml.cml.base.CMLUtil;
+
+public class BVARElementTest {
+
+	@Test
+	public void testBVarInScope0() {
+		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
+				"  <m:bvar><ci>b</ci></m:bvar>" +
+				"  <m:apply>" +
+				"    <m:sin/>" +
+				"    <m:ci>a</m:ci>" +
+				"  </m:apply>" +
+				"</m:lambda>";
+		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss);
+		CIElement ci = (CIElement) lambda.getChild(2).getChild(1);
+		Assert.assertTrue("in scope", ci.hasBvarInScope());
+	}
+
+	@Test
+	public void testBVarInScope() {
+		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
+				"  <m:bvar><ci>b</ci></m:bvar>" +
+				"  <m:apply>" +
+				"    <m:sin/>" +
+				"    <m:ci>b</m:ci>" +
+				"  </m:apply>" +
+				"</m:lambda>";
+		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss);
+		CIElement ci = (CIElement) lambda.getChild(2).getChild(1);
+		Assert.assertTrue("in scope", ci.hasBvarInScope());
+	}
+
+	@Test
+	public void testBVarInScope1() {
+		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
+				"  <m:bvar><ci>b</ci></m:bvar>" +
+				"  <m:apply>" +
+				"    <m:sin/>" +
+				"    <m:ci>c</m:ci>" +
+				"  </m:apply>" +
+				"</m:lambda>";
+		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss);
+		CIElement ci = (CIElement) lambda.getChild(2).getChild(1);
+		Assert.assertFalse("in scope", ci.hasBvarInScope());
+	}
+
+	
+
+
+}

File src/test/java/org/xmlcml/mathml/CIElementTest.java

 	@Test
 	public void testDifferentiateWithBVar() {
 		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>x</m:bvar>" +
+				"  <m:bvar><ci>x</ci></m:bvar>" +
 				"  <m:apply>" +
 				"    <m:times/>" +
 				"    <m:ci>x</m:ci>" +
 	@Test
 	public void testDifferentiateWithBVar1() {
 		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>x</m:bvar>" +
+				"  <m:bvar><ci>x</ci></m:bvar>" +
 				"  <m:apply>" +
 				"    <m:times/>" +
 				"    <m:apply>" +

File src/test/java/org/xmlcml/mathml/COSTest.java

 package org.xmlcml.mathml;
 
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.xmlcml.cml.base.CMLUtil;
 import org.xmlcml.mathml.AbstractMathElement;
 	}
 
 	@Test
+	@Ignore
 	public void testDifferentiate() {
 		String ss = "<m:apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
 				"<m:cos/>" +

File src/test/java/org/xmlcml/mathml/LAMBDATest.java

 	@Test
 	public void testLambda() {
 		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
 				"  <m:apply>" +
 				"    <m:sin/>" +
 				"    <m:cn>0</m:cn>" +
 	@Test
 	public void testLambda1() {
 		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
-				"  <m:bvar>b</m:bvar>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
+				"  <m:bvar><ci>b</ci></m:bvar>" +
 				"  <m:apply>" +
 				"    <m:sin/>" +
 				"    <m:cn>0</m:cn>" +
 	@Test
 	public void testLambda2() {
 		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
-				"  <m:bvar>a</m:bvar>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
 				"  <m:apply>" +
 				"    <m:sin/>" +
 				"    <m:cn>0</m:cn>" +
 	@Test
 	public void testLambda3() {
 		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
-				"  <m:bvar>b</m:bvar>" +
+				"  <m:bvar><ci>a</ci></m:bvar>" +
+				"  <m:bvar><ci>b</ci></m:bvar>" +
 				"  <m:apply>" +
 				"    <m:sin/>" +
 				"    <m:ci>a</m:ci>" +
 	}
 
 	@Test
-	public void testBVarInScope0() {
-		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
-				"  <m:bvar>b</m:bvar>" +
-				"  <m:apply>" +
-				"    <m:sin/>" +
-				"    <m:ci>a</m:ci>" +
-				"  </m:apply>" +
-				"</m:lambda>";
-		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss);
-		CIElement ci = (CIElement) lambda.getChild(2).getChild(1);
-		Assert.assertTrue("in scope", ci.hasBvarInScope());
-	}
-
-	@Test
-	public void testBVarInScope() {
-		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
-				"  <m:bvar>b</m:bvar>" +
-				"  <m:apply>" +
-				"    <m:sin/>" +
-				"    <m:ci>b</m:ci>" +
-				"  </m:apply>" +
-				"</m:lambda>";
-		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss);
-		CIElement ci = (CIElement) lambda.getChild(2).getChild(1);
-		Assert.assertTrue("in scope", ci.hasBvarInScope());
-	}
-
-	@Test
-	public void testBVarInScope1() {
-		String ss = "<m:lambda xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"  <m:bvar>a</m:bvar>" +
-				"  <m:bvar>b</m:bvar>" +
-				"  <m:apply>" +
-				"    <m:sin/>" +
-				"    <m:ci>c</m:ci>" +
-				"  </m:apply>" +
-				"</m:lambda>";
-		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss);
-		CIElement ci = (CIElement) lambda.getChild(2).getChild(1);
-		Assert.assertFalse("in scope", ci.hasBvarInScope());
-	}
-
-	
-	@Test
 	public void testDifferentiate() {
 		String ss = "<m:apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
 				"  <m:exp/>" +

File src/test/java/org/xmlcml/mathml/MINUSTest.java

+package org.xmlcml.mathml;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlcml.cml.base.CMLUtil;
+import org.xmlcml.mathml.AbstractMathElement;
+
+public class MINUSTest {
+
+	@Test
+	public void testPlus() {
+		String ss = "<m:apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"<m:minus/>" +
+				"<m:cn>4</m:cn>" +
+				"<m:cn>3</m:cn>" +
+				"</m:apply>";
+		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
+		Object o = ci.eval();
+		Assert.assertNotNull("object", o);
+		Assert.assertTrue("class", o instanceof Number);
+		Assert.assertEquals("object", 1, (Number) o);
+	}
+	
+	@Test
+	public void testPlusPlus() {
+		String ss = "<m:apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"<m:minus/>" +
+				"<m:apply>" +
+				"<m:minus/>" +
+				"<m:cn>4</m:cn>" +
+				"<m:cn>3</m:cn>" +
+				"</m:apply>"+
+				"<m:apply>" +
+				"<m:minus/>" +
+				"<m:cn>2</m:cn>" +
+				"<m:cn>7</m:cn>" +
+				"</m:apply>"+
+				"</m:apply>";
+		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
+		Object o = ci.eval();
+		Assert.assertNotNull("object", o);
+		Assert.assertTrue("class", o instanceof Number);
+		Assert.assertEquals("object", 6, (Number) o);
+	}
+	
+	@Test
+	public void testDifferentiate() {
+		String ss = "<m:apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"<m:minus/>" +
+				"<m:cn>a</m:cn>" +
+				"<m:cn>b</m:cn>" +
+				"</m:apply>";
+		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
+		Differentiable diff = ci.castToDifferentiable();
+		Assert.assertNotNull("object", diff);
+		AbstractMathElement result = diff.differentiate();
+		// fragile
+		Assert.assertEquals("exp ", 
+				"<apply xmlns=\"http://www.w3.org/1998/Math/MathML\">" +
+				"<minus /><cn>0</cn><cn>0</cn></apply>", 
+				result.toXML());
+	}
+	
+	@Test
+	public void testTidyZero() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:minus/>" +
+				"      <m:cn>0</m:cn>" +
+				"      <m:cn>b</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		MINUSElement.tidyZero(math);
+		Assert.assertEquals("tidy", 
+				"<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><apply><minus /><cn>b</cn></apply></apply></math>", math.toXML());
+	}
+	
+	@Test
+	public void testTidyZero1() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:minus/>" +
+				"      <m:cn>b</m:cn>" +
+				"      <m:cn>0</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		MINUSElement.tidyZero(math);
+		Assert.assertEquals("tidy", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><cn>b</cn></apply></math>", math.toXML());
+	}
+}

File src/test/java/org/xmlcml/mathml/PLUSTest.java

 
 import org.junit.Assert;
 import org.junit.Test;
+import org.xmlcml.cml.base.CMLUtil;
 import org.xmlcml.mathml.AbstractMathElement;
 
 public class PLUSTest {
 				result.toXML());
 	}
 	
+	@Test
+	public void testTidyZero() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:plus/>" +
+				"      <m:cn>0</m:cn>" +
+				"      <m:cn>b</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		PLUSElement.tidyZero(math);
+		Assert.assertEquals("tidy", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><cn>b</cn></apply></math>", math.toXML());
+	}
 }

File src/test/java/org/xmlcml/mathml/POWERTest.java

 		String ss = "" +
 			"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
 			"  <m:lambda>" +
-			"    <m:bvar>A</m:bvar>" +
+			"    <m:bvar><ci>A</ci></m:bvar>" +
 			"    <m:apply>" +
 			"      <m:power/>" +
 			"      <m:ci>A</m:ci>" +
 				"<apply xmlns=\"http://www.w3.org/1998/Math/MathML\"><times /><cn>3</cn><apply><times /><cn>1</cn><apply><power /><ci>A</ci><cn>2</cn></apply></apply></apply>",
 					o.toXML());
 	}
+	
+	@Test
+	public void testTidyZero() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:power/>" +
+				"      <m:cn>b</m:cn>" +
+				"      <m:cn>0</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		POWERElement.tidyZero(math);
+		Assert.assertEquals("tidy", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><cn>1</cn></apply></math>", math.toXML());
+	}
+	
+	@Test
+	public void testTidyZero1() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:power/>" +
+				"      <m:cn>0</m:cn>" +
+				"      <m:cn>b</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		POWERElement.tidyZero(math);
+		Assert.assertEquals("tidy", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><cn>0</cn></apply></math>", math.toXML());
+	}
 }

File src/test/java/org/xmlcml/mathml/SUMTest.java

+package org.xmlcml.mathml;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class SUMTest {
+
+	@Test
+	@Ignore
+	public void testSum() {
+	    String ss = "" +
+	    "<apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+	    "   <sum/>" +
+	    "   <bvar>" +
+	    "      <ci> x </ci>" +
+	    "   </bvar>" +
+	    "   <lowlimit>" +
+	    "      <ci> a </ci>" +
+	    "   </lowlimit>" +
+	    "   <uplimit>" +
+	    "      <ci> b </ci>" +
+	    "   </uplimit>" +
+	    "   <apply>" +
+	    "      <ci type='fn'> f </ci>" +
+	    "      <ci> x </ci>" +
+	    "   </apply>" +
+	    "</apply> " +
+	    "";
+		APPLYElement apply = (APPLYElement) AbstractMathElement.createElementFromXML(ss);
+		Object o = apply.eval();
+		Assert.assertNotNull("object", o);
+		Assert.assertTrue("class", o instanceof Double);
+		Assert.assertEquals("object", 7.38905609893065, (double)(Double) o, 0.000001);
+		
+	}
+	
+	@Test
+	@Ignore
+	public void testSum1() {
+			String ss = "" +
+		"<apply xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+	    "  <sum/>" +
+	    "  <bvar>" +
+	    "    <ci> x </ci>" +
+	    "  </bvar>" +
+	    "  <condition>" +
+	    "    <apply>" +
+	    "      <in/>" +
+	    "      <ci> x </ci>" +
+	    "      <ci type='set'> B </ci>" +
+	    "    </apply>" +
+	    "  </condition>" +
+	    "  <apply>" +
+	    "    <ci type='fn'> f </ci>" +
+	    "    <ci> x </ci>" +
+	    "  </apply>" +
+	    "</apply> " +
+					"";
+		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
+		Object o = ci.eval();
+		Assert.assertNotNull("object", o);
+		Assert.assertTrue("class", o instanceof Double);
+		Assert.assertEquals("object", 7.38905609893065, (double)(Double) o, 0.000001);
+		
+	}
+}
+	

File src/test/java/org/xmlcml/mathml/TIMESTest.java

 		AbstractMathElement result = diff.differentiate();
 	}
 	
+	@Test
+	public void testTidyZero() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:times/>" +
+				"      <m:cn>0</m:cn>" +
+				"      <m:cn>b</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		TIMESElement.tidyZero(math);
+		Assert.assertEquals("tidy", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><cn>0</cn></apply></math>", math.toXML());
+	}
+	
+	@Test
+	public void testTidyOne() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:apply>" +
+				"    <m:plus/>" +
+				"    <m:cn>a</m:cn>" +
+				"    <m:apply>" +
+				"      <m:times/>" +
+				"      <m:cn>1</m:cn>" +
+				"      <m:cn>b</m:cn>" +
+				"    </m:apply>" +
+				"  </m:apply>" +
+				"</m:math>";
+		MATHElement math = (MATHElement) AbstractMathElement.createElementFromXML(ss);
+		TIMESElement.tidyOne(math);
+		Assert.assertEquals("tidy", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><apply><plus /><cn>a</cn><cn>b</cn></apply></math>", math.toXML());
+	}
 }

File src/test/java/org/xmlcml/mathml/UnsortedTests.java

 import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.xmlcml.cml.base.CMLUtil;
 
 
 public class UnsortedTests {
 		"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>"+
 		"  <m:lambda>"+
 		"    <m:bvar>R</m:bvar>"+
-		"<m:apply>"+
-		"<m:minus/>"+
-		"<m:ci>"+
-		"<m:apply>"+
-		"<m:times/>"+
-		"<m:ci>a</m:ci>"+
-		"<m:ci>"+
-		"<m:apply>"+
-		"<m:exp/>"+
-		"<m:ci>"+
-		"<m:apply>"+
-		"<m:divide/>"+
-		"<m:ci>"+
-		"<m:apply>"+
-		"<m:minus/>"+
-		"<m:ci>R</m:ci>"+
-		"</m:apply>"+
-		"</m:ci>"+
-		"<m:ci>roh</m:ci>"+
-		"</m:apply>"+
-		"</m:ci>"+
-		"</m:apply>"+
-		"</m:ci>"+
-		"</m:apply>"+
-		"</m:ci>"+
-		"<m:ci>"+
-		"<m:apply>"+
-		"<m:divide/>"+
-		"<m:ci>c</m:ci>"+
-		"<m:ci>"+
-		"<m:apply>"+
-		"<m:power/>"+
-		"<m:ci>R</m:ci>"+
-		"<m:cn>6</m:cn>"+
-		"</m:apply>"+
-		"</m:ci>"+
-		"</m:apply>"+
-		"</m:ci>"+
-		"</m:apply>"+
-		"</m:lambda>"+
+		"    <m:apply>"+
+		"      <m:minus/>"+
+		"      <m:apply>"+
+		"        <m:times/>"+
+		"        <m:ci>a</m:ci>"+
+		"        <m:apply>"+
+		"          <m:exp/>"+
+		"          <m:apply>"+
+		"            <m:divide/>"+
+		"            <m:apply>"+
+		"              <m:minus/>"+
+		"              <m:ci>R</m:ci>"+
+		"            </m:apply>"+
+		"            <m:ci>roh</m:ci>"+
+		"          </m:apply>"+
+		"        </m:apply>"+
+		"      </m:apply>"+
+		"      <m:apply>"+
+		"        <m:divide/>"+
+		"        <m:ci>c</m:ci>"+
+		"        <m:apply>"+
+		"          <m:power/>"+
+		"          <m:ci>R</m:ci>"+
+		"          <m:cn>6</m:cn>"+
+		"        </m:apply>"+
+		"      </m:apply>"+
+		"    </m:apply>"+
+		"  </m:lambda>"+
 		"</m:math>"+
 		"";
 		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
 	public void testEvalCompleteContent() {
 		String ss = "" +
 		"<m:apply xmlns:m='http://www.w3.org/1998/Math/MathML'>"+
-		"<m:minus/>"+
-		"<m:apply>"+
-		"<m:times/>"+
-		"<m:ci>a</m:ci>"+
-		"<m:apply>"+
-		"<m:exp/>"+
-		"<m:apply>"+
-		"<m:divide/>"+
-		"<m:apply>"+
-		"<m:minus/>"+
-		"<m:ci>R</m:ci>"+
-		"</m:apply>"+
-		"<m:ci>roh</m:ci>"+
-		"</m:apply>"+
-		"</m:apply>"+
-		"</m:apply>"+
-		"<m:apply>"+
-		"<m:divide/>"+
-		"<m:ci>c</m:ci>"+
-		"<m:apply>"+
-		"<m:power/>"+
-		"<m:ci>R</m:ci>"+
-		"<m:cn>6</m:cn>"+
-		"</m:apply>"+
-		"</m:apply>"+
+		"  <m:minus/>"+
+		"  <m:apply>"+
+		"    <m:times/>"+
+		"    <m:ci>a</m:ci>"+
+		"    <m:apply>"+
+		"      <m:exp/>"+
+		"      <m:apply>"+
+		"        <m:divide/>"+
+		"        <m:apply>"+
+		"          <m:minus/>"+
+		"          <m:ci>R</m:ci>"+
+		"        </m:apply>"+
+		"        <m:ci>roh</m:ci>"+
+		"      </m:apply>"+
+		"    </m:apply>"+
+		"  </m:apply>"+
+		"  <m:apply>"+
+		"    <m:divide/>"+
+		"    <m:ci>c</m:ci>"+
+		"    <m:apply>"+
+		"      <m:power/>"+
+		"      <m:ci>R</m:ci>"+
+		"      <m:cn>6</m:cn>"+
+		"    </m:apply>"+
+		"  </m:apply>"+
 		"</m:apply>"+
 		"";
 		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
 	public void testEval2() {
 		String ss = "" +
 				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
-				"<m:apply>" +
-				"<m:eq/>"+
-				"<m:ci>a</m:ci>" +
-				"<m:cn>3</m:cn>" +
-				"</m:apply>"+
-				"<m:apply>" +
-				"<m:eq/>" +
-				"<m:ci>b</m:ci>"+
-				"<m:ci>a</m:ci>"+
-				"</m:apply>"+
+				"  <m:apply>" +
+				"    <m:eq/>"+
+				"    <m:ci>a</m:ci>" +
+				"    <m:cn>3</m:cn>" +
+				"  </m:apply>"+
+				"  <m:apply>" +
+				"    <m:eq/>" +
+				"    <m:ci>b</m:ci>"+
+				"    <m:ci>a</m:ci>"+
+				"  </m:apply>"+
 				"</m:math>"+
 				"";
 		AbstractMathElement ci = AbstractMathElement.createElementFromXML(ss);
 				"  </m:apply>"+
 				"</m:math>"+
 				"";
-			APPLYElement ci = (APPLYElement) AbstractMathElement.createElementFromXML(ss).getChildElements().get(0);
-			AbstractMathElement o = ci.differentiate();
-			Assert.assertNotNull("object", o);
-			Assert.assertEquals("object", "<apply xmlns=\"http://www.w3.org/1998/Math/MathML\">" +
-					"<times /><cn>0.5</cn>" +
-					"<apply><times /><apply><plus /><apply><times /><cn>2</cn>" +
-					"<apply><times /><cn>0</cn><apply><power /><ci>a</ci><cn>1</cn></apply></apply></apply><apply><times /><cn>2</cn><apply><times /><cn>0</cn><apply><power /><ci>b</ci><cn>1</cn></apply></apply></apply></apply><apply><power /><apply><plus /><apply><power /><ci>a</ci><cn>2</cn></apply><apply><power /><ci>b</ci><cn>2</cn></apply></apply><cn>-0.5</cn></apply></apply></apply>", o.toXML());
+		APPLYElement ci = (APPLYElement) AbstractMathElement.createElementFromXML(ss).getChildElements().get(0);
+		AbstractMathElement diff = ci.differentiate();
+		Assert.assertEquals("object", "<apply xmlns=\"http://www.w3.org/1998/Math/MathML\">" +
+				"<times /><cn>0.5</cn>" +
+				"<apply><times /><apply><plus /><apply><times /><cn>2</cn>" +
+				"<apply><times /><cn>0</cn><apply><power /><ci>a</ci><cn>1</cn></apply></apply></apply><apply><times /><cn>2</cn><apply><times /><cn>0</cn><apply><power /><ci>b</ci><cn>1</cn></apply></apply></apply></apply><apply><power /><apply><plus /><apply><power /><ci>a</ci><cn>2</cn></apply><apply><power /><ci>b</ci><cn>2</cn></apply></apply><cn>-0.5</cn></apply></apply></apply>", diff.toXML());
+		diff.tidy();
+		Assert.assertEquals("object", "<apply xmlns=\"http://www.w3.org/1998/Math/MathML\">" +
+				"<times /><cn>0.5</cn><cn>0</cn></apply>", diff.toXML());
 	}
 	
+	@Test
+	public void testPythagorasDifferentiateBvar() {
+		String ss = "" +
+				"<m:math xmlns:m='http://www.w3.org/1998/Math/MathML'>" +
+				"  <m:lambda>" +
+				"    <m:bvar><ci>a</ci></m:bvar>" +
+				"    <m:apply>" +
+				"      <m:power/>"+
+				"      <m:apply>" +
+				"        <m:plus/>" +
+				"        <m:apply>" +
+				"          <m:power/>" +
+				"          <m:ci>a</m:ci>"+
+				"          <m:cn>2</m:cn>"+
+				"        </m:apply>"+
+				"        <m:apply>" +
+				"          <m:power/>" +
+				"          <m:ci>b</m:ci>"+
+				"          <m:cn>2</m:cn>"+
+				"        </m:apply>"+
+				"      </m:apply>" +
+				"      <m:cn>0.5</m:cn>"+
+				"    </m:apply>" +
+				"  </m:lambda>"+
+				"</m:math>"+
+				"";
+		LAMBDAElement lambda = (LAMBDAElement) AbstractMathElement.createElementFromXML(ss).getChildElements().get(0);
+		AbstractMathElement diff = lambda.differentiate();
+		
+		diff.tidy();
+		Assert.assertEquals("object", "<apply xmlns=\"http://www.w3.org/1998/Math/MathML\"><times /><cn>0.5</cn><apply><times /><apply><times /><cn>2</cn><ci>a</ci></apply><apply><power /><apply><plus /><apply><power /><ci>a</ci><cn>2</cn></apply><apply><power /><ci>b</ci><cn>2</cn></apply></apply><cn>-0.5</cn></apply></apply></apply>", diff.toXML());
+	}
 
 }