~johanvandegriff/Stoichiometry

c7f71f7ba8806c072047c0499d90717f3cbc2805 — johanvandegriff 4 years ago a5b8746
added charge balancing
changed the way compound strings are stored to accommodate grouping
accounted for electron mass
M src/stoichiometry/Compound.java => src/stoichiometry/Compound.java +40 -29
@@ 7,17 7,19 @@ import java.util.Map;

public class Compound {
	private final Map<Element, Integer> elements;
	private final String stateOfMatter;
	private final String displayString;
	private final int charge;

	public Compound(Map<Element, Integer> elements, String stateOfMatter, int charge) {
	public Compound(Map<Element, Integer> elements, String displayString, int charge) {
		this.elements = elements;
		this.stateOfMatter = stateOfMatter;
		this.displayString = displayString;
		this.charge = charge;
	}

	public static Compound fromString(String compound) {
//		System.out.println("c Input	" + compound);
	    String displayString = compound.replaceAll("\\s+","");
	    
		List<String> parts = new ArrayList<>();

		// loop through the chars of the compound String


@@ 51,7 53,7 @@ public class Compound {

		//System.out.println("Split	" + parts);

		String stateOfMatter = "";
//		String stateOfMatter = "";
		int charge = 0;
		
		int i = 0;


@@ 81,7 83,7 @@ public class Compound {
				if (subList.size() == 1){
					String s = subList.get(0);
					if(s.equals("s") || s.equals("l") || s.equals("g") || s.equals("aq")){
						stateOfMatter = s;
//						stateOfMatter = s;
						quantity = 0;
					} else
					if(s.equals("-")){


@@ 157,7 159,7 @@ public class Compound {
		}

		//System.out.println("Final	" + elements);
		return new Compound(elements, stateOfMatter, charge);
		return new Compound(elements, displayString, charge);
	}

	public Map<Element, Integer> getElements() {


@@ 165,32 167,37 @@ public class Compound {
	}
	
	public String chargeString(){
		if (charge > 0){
			return "(" + charge + "+)";
		} else if (charge < 0) {
			return "(" + -charge + "-)";
		} else {
			return "";
		}
	    if (charge < -1) {
            return "(" + -charge + "-)";
	    } else if (charge == -1) {
            return "(-)";
        } else if (charge == 1) {
            return "(+)";
        } else if (charge > 1) {
            return "(" + charge + "+)";
        } else { //charge == 0
	        return "";
	    }
	}
	
	@Override
	public String toString() {
		String s = "";
		for (Map.Entry<Element, Integer> entry : elements.entrySet()){
			Element e = entry.getKey();
			int q = entry.getValue();
			if (q == 1) {
				s += e;
			} else if (q != 0){
				s += "" + e + q;
			}
		}
		s += chargeString();
		if (stateOfMatter != "") {
			s += "(" + stateOfMatter + ")";
		}
		return s;
	    return displayString;
//		String s = "";
//		for (Map.Entry<Element, Integer> entry : elements.entrySet()){
//			Element e = entry.getKey();
//			int q = entry.getValue();
//			if (q == 1) {
//				s += e;
//			} else if (q != 0){
//				s += "" + e + q;
//			}
//		}
//		s += chargeString();
//		if (displayString != "") {
//			s += "(" + displayString + ")";
//		}
//		return s;
	}

	public double getMolarMass() {


@@ 200,6 207,10 @@ public class Compound {
			int q = entry.getValue();
			m += e.getAtomicMass() * q;
		}
		return m;
		return m - charge * Element.ELECTRON_ATOMIC_MASS; //account for the mass of electrons
	}

    public int getCharge() {
        return charge;
    }
}

M src/stoichiometry/Element.java => src/stoichiometry/Element.java +2 -0
@@ 119,6 119,8 @@ public enum Element {
	Lv("Livermorium", 116, 298),
	Uus("Ununseptium", 117, 294),
	Uuo("Ununoctium", 118, 294);
	
	public static final double ELECTRON_ATOMIC_MASS = 5.48579909070e-4;

	private final String fullName;
	private final int atomicNumber;

M src/stoichiometry/Main.java => src/stoichiometry/Main.java +6 -2
@@ 7,7 7,10 @@ import numbers.Solver;

public class Main {
	public static void main(String[] args){
		Reaction.fromString("H2+O2->H2O").printInfo();
        Reaction.fromString("H2 + O2 => H2O").printInfo();
        Reaction.fromString("Fe(l) + 2 H2O(l) -> Fe(2+)(aq) + OH(-)(aq) + H2(g)").printInfo();
        Reaction.fromString("C3H6(OH)2 + O2 -> CO2 + H2O").printInfo();
        Reaction.fromString("H(+) + Cl(-) -> HCl").printInfo();
//		Reaction.fromString("Al + I2 -> AlI3").printInfo();
//		Reaction.fromString("NH3 + CO2 → (NH2)2CO + H2O").printInfo();
//		Reaction.fromString("Zn + HCl → ZnCl2 + H2").printInfo();


@@ 50,8 53,9 @@ public class Main {
//        System.out.println(Compound.fromString("Ni").getMolarMass());
//		System.out.println(Compound.fromString("NaCl").getMolarMass());
//		System.out.println(Compound.fromString("MgSO4(H2O)7").getMolarMass());
		Reaction.fromString("CO2(g) + H2O(l) ⇆ H3O(+)(aq) + HCO3(-)(aq) ").printInfo();
//		Reaction.fromString("CO2(g) + H2O(l) ⇆ H3O(+)(aq) + HCO3(-)(aq) ").printInfo();

//      System.out.println(Compound.fromString("NaOH").getMolarMass());
		
//		System.out.println(new StringTableBuilder().addItem("A1").addItem("A2").addItem("A3").newRow().addItem("B1").addItem("B2").newRow().addItem("C1").build(4));
		

M src/stoichiometry/Reaction.java => src/stoichiometry/Reaction.java +358 -340
@@ 16,352 16,370 @@ import numbers.Solver;
import output.StringTable.Align;

public class Reaction {
	private final Expression reactants, products;

	public Reaction(Expression reactants, Expression products) {
		this.reactants = reactants;
		this.products = products;
	}
	
//	public static List<String> splitPlus(String input){
//		List<String> parts = new ArrayList<>();
//		String part = "";
//		int parens = 0;
//		for (int i = 0; i < input.length(); i++) {
//			char ch = input.charAt(i);
//			if (ch == '+' && parens == 0){
//				parts.add(part);
//				part = "";
//			} else {
//				if (ch == '('){
//					parens++;
//				}
//				if (ch == ')'){
//					parens--;
//				}
//				part += ch;
//			}
//		}
//		parts.add(part);
//		return parts;
//	}
	
	public static Reaction fromString(String reaction) {
//		System.out.println("r Input	" + reaction);
		
//		String reaction2 = "";
//		for (int i = 0; i < reaction.length(); i++) {
//			char ch = reaction.charAt(i);
//			if (ch != ' ') {
//				reaction2 += ch;
//			}
//		}
		//System.out.println("!Spaces	" + reaction2);

		String[] leftRight = reaction.split("->|=>|<-|<=|<|>|<<|>>|<>|<->|<=>|=|←|→|↔|«|»|⇆");
		
		//System.out.println("Sides	" + Arrays.asList(leftRight));
		
//		List<String> reactantsString = splitPlus(leftRight[0]); //Arrays.asList(leftRight[0].split("\\+"));
//		List<String> productsString = splitPlus(leftRight[1]); //Arrays.asList(leftRight[1].split("\\+"));

//		Map<Compound, Integer> reactants = new LinkedHashMap<>();
//		Map<Compound, Integer> products = new LinkedHashMap<>();
//		for (String s : reactantsString) {
//			reactants.put(Compound.fromString(s), 1);
//		}
//		for (String s : productsString) {
//			products.put(Compound.fromString(s), 1);
//		}

		Expression reactants = Expression.fromString(leftRight[0]);
		Expression products = Expression.fromString(leftRight[1]);
		
//		System.out.println("Unsolved	" + reactants + " => " + products);

		Map<Compound, Integer> all = new LinkedHashMap<>();
		all.putAll(reactants.getCompounds());
		all.putAll(products.getCompounds());

		Set<Element> elements = new HashSet<>();
		for (Map.Entry<Compound, Integer> entry : all.entrySet()){
			Compound compound = entry.getKey();
			for (Entry<Element, Integer> entry1 : compound.getElements().entrySet()) {
				elements.add(entry1.getKey());
			}
		}
		
//		System.out.println("Elements	" + elements);
		
		Fraction [][]mat = Fraction.new2DArray(1+elements.size(), reactants.size() + products.size());
		mat[0][0] = Fraction.integer(1);
//		for (int i=0; i<mat.length; i++){
//			for (int j=0; j<mat[0].length; j++)
//				System.out.print(mat[i][j] + " ");
//			System.out.println();
//		}
		int i = 0;
//		System.out.println("[1, 0, ... 0] = [1]");
		for (Element e : elements) {
//			List<Integer> factors = countFactors(e, reactants);
//			factors.addAll(negative(countFactors(e, products)));
			List<Integer> factors = reactants.countFactors(e);
			factors.addAll(negative(products.countFactors(e)));
//			System.out.println(factors + " = [0] " + e);
			for (int j=0; j<factors.size(); j++) {
				mat[i+1][j] = Fraction.integer(factors.get(j));
			}
			i++;
		}
		

//		System.out.println(reactants);
//		System.out.println(products);
//		System.out.println(elements);

		Fraction [][]constants = Fraction.new2DArray(1+elements.size(), 1);
		constants[0][0] = Fraction.integer(1);

		
		
    private final Expression reactants, products;

    public Reaction(Expression reactants, Expression products) {
        this.reactants = reactants;
        this.products = products;
    }

    //	public static List<String> splitPlus(String input){
    //		List<String> parts = new ArrayList<>();
    //		String part = "";
    //		int parens = 0;
    //		for (int i = 0; i < input.length(); i++) {
    //			char ch = input.charAt(i);
    //			if (ch == '+' && parens == 0){
    //				parts.add(part);
    //				part = "";
    //			} else {
    //				if (ch == '('){
    //					parens++;
    //				}
    //				if (ch == ')'){
    //					parens--;
    //				}
    //				part += ch;
    //			}
    //		}
    //		parts.add(part);
    //		return parts;
    //	}

    public static Reaction fromString(String reaction) {
        //		System.out.println("r Input	" + reaction);

        //		String reaction2 = "";
        //		for (int i = 0; i < reaction.length(); i++) {
        //			char ch = reaction.charAt(i);
        //			if (ch != ' ') {
        //				reaction2 += ch;
        //			}
        //		}
        //System.out.println("!Spaces	" + reaction2);

        String[] leftRight = reaction.split("->|=>|<-|<=|<|>|<<|>>|<>|<->|<=>|=|←|→|↔|«|»|⇆");

        //System.out.println("Sides	" + Arrays.asList(leftRight));

        //		List<String> reactantsString = splitPlus(leftRight[0]); //Arrays.asList(leftRight[0].split("\\+"));
        //		List<String> productsString = splitPlus(leftRight[1]); //Arrays.asList(leftRight[1].split("\\+"));

        //		Map<Compound, Integer> reactants = new LinkedHashMap<>();
        //		Map<Compound, Integer> products = new LinkedHashMap<>();
        //		for (String s : reactantsString) {
        //			reactants.put(Compound.fromString(s), 1);
        //		}
        //		for (String s : productsString) {
        //			products.put(Compound.fromString(s), 1);
        //		}

        Expression reactants = Expression.fromString(leftRight[0]);
        Expression products = Expression.fromString(leftRight[1]);

        //		System.out.println("Unsolved	" + reactants + " => " + products);

        Map<Compound, Integer> all = new LinkedHashMap<>();
        all.putAll(reactants.getCompounds());
        all.putAll(products.getCompounds());

        Set<Element> elements = new HashSet<>();
        for (Map.Entry<Compound, Integer> entry : all.entrySet()) {
            Compound compound = entry.getKey();
            for (Entry<Element, Integer> entry1 : compound.getElements().entrySet()) {
                elements.add(entry1.getKey());
            }
        }

        //		System.out.println("Elements	" + elements);

        int extraRows = 1;

        for (Entry<Compound, Integer> e : reactants.getCompounds().entrySet()) {
            if (e.getKey().getCharge() != 0) extraRows = 2;
        }
        for (Entry<Compound, Integer> e : products.getCompounds().entrySet()) {
            if (e.getKey().getCharge() != 0) extraRows = 2;
        }

        Fraction[][] mat = Fraction.new2DArray(extraRows + elements.size(), reactants.size() + products.size());
        mat[0][0] = Fraction.integer(1);

        //add an equation so that the sum of the charges of the reactants and the negatives of the
        //charges of the products equal zero.
        int n = 0;

        if (extraRows == 2) {
            for (Entry<Compound, Integer> e : reactants.getCompounds().entrySet()) {
                mat[1][n] = Fraction.integer(e.getKey().getCharge());
                n++;
            }
            for (Entry<Compound, Integer> e : products.getCompounds().entrySet()) {
                mat[1][n] = Fraction.integer(-e.getKey().getCharge());
                n++;
            }
        }

        n = 0;
        //		System.out.println("[1, 0, ... 0] = [1]");
        for (Element e : elements) {
            //			List<Integer> factors = countFactors(e, reactants);
            //			factors.addAll(negative(countFactors(e, products)));
            List<Integer> factors = reactants.countFactors(e);
            factors.addAll(negative(products.countFactors(e)));
            //			System.out.println(factors + " = [0] " + e);
            for (int j = 0; j < factors.size(); j++) {
                mat[n + extraRows][j] = Fraction.integer(factors.get(j));
            }
            n++;
        }

        //		System.out.println(reactants);
        //		System.out.println(products);
        //		System.out.println(elements);

        Fraction[][] constants = Fraction.new2DArray(extraRows + elements.size(), 1);
        constants[0][0] = Fraction.integer(1);

//        for (int i = 0; i < mat.length; i++) {
//            System.out.print(constants[i][0] + " | ");
//            for (int j = 0; j < mat[0].length; j++) {
//                System.out.print(mat[i][j] + " ");
//            }
//            System.out.println();
//        }

        List<Fraction> coefficients = Solver.solve(mat, constants);
        //System.out.println(coefficients);
        

        int lowestCommonDenominator = Fraction.lowestCommonDenominator(coefficients);
//        for (Fraction c : coefficients) {
//        	lowestCommonDenominator *= c.getDenominator();
//        }
        
//    	System.out.println(lowestCommonDenominator);
//        System.out.println(coefficients);
        
        //        for (Fraction c : coefficients) {
        //        	lowestCommonDenominator *= c.getDenominator();
        //        }

        //    	System.out.println(lowestCommonDenominator);
        //        System.out.println(coefficients);

        Iterator<Fraction> it1 = coefficients.iterator();
        Iterator<Map.Entry<Compound, Integer>> it2 = reactants.getCompounds().entrySet().iterator();
        Iterator<Map.Entry<Compound, Integer>> it3 = products.getCompounds().entrySet().iterator();
        
        

        while (it1.hasNext()) {
//        	System.out.print(a);
        	int coefficient = it1.next().multiplySelfBy(lowestCommonDenominator).getNumerator();
//        	System.out.println(coefficient);
        	if (it2.hasNext()) {
        		it2.next().setValue(coefficient);
        	} else if (it3.hasNext()){
        		it3.next().setValue(coefficient);
        	}
            //        	System.out.print(a);
            int coefficient = it1.next().multiplySelfBy(lowestCommonDenominator).getNumerator();
            //        	System.out.println(coefficient);
            if (it2.hasNext()) {
                it2.next().setValue(coefficient);
            } else if (it3.hasNext()) {
                it3.next().setValue(coefficient);
            }
        }
        
//        
//        int j = 0;
//        for ( entry : reactants.entrySet()){
//        	
//        	j++;
//        }
//        for (int j=0; j<coefficients.size(); j++) {
//        	if (j < reactants.size()) {
//        		reactants.set();
//        	} else {
//        		
//        	}
//        }
		//System.out.println("Solved		" + reactants + " => " + products);
		return new Reaction(reactants, products); //, false);
	}
	
	private static List<Integer> negative(List<Integer> list) {
		List<Integer> negative = new ArrayList<>();
		for (int i : list) {
			negative.add(-i);
		}
		return negative;
	}

//	private static List<Integer> countFactors(Element e, Map<Compound, Integer> compounds){
//		List<Integer> factors = new ArrayList<>();
//		for (Map.Entry<Compound, Integer> entry : compounds.entrySet()){
//			Compound compound = entry.getKey();
//			if (compound.getElements().containsKey(e)){
//				factors.add(compound.getElements().get(e));
//			} else {
//				factors.add(0);
//			}
//		}
//		return factors;
//	}

	public Expression getReactants() {
		return reactants;
	}

	public Expression getProducts() {
		return products;
	}
	
	public static String getCompoundsString(Map<Compound, Integer> compounds, boolean grams){
		//TODO use a StringBuilder for Reaction.getCompoundsString()
		String s = "";
		boolean first = true;
		for (Map.Entry<Compound, Integer> entry : compounds.entrySet()){
			Compound compound = entry.getKey();
			int quantity = entry.getValue();
			if (quantity != 0) {
				if (first) {
					first = false;
				} else {
					s += " + ";
				}
				if (quantity != 1 || grams) {
					if (grams) {
						s += quantity + "g ";
					} else {
						s += quantity + " ";
					}
				}
				s += compound;
			}
		}
		return s;
	}
	
	@Override
	public String toString() {
		return reactants + " => " + products;
	}
	
//	public static Map<Compound, Fraction> toGrams(Map<Compound, Integer> compounds){
//		Map<Compound, Fraction> compoundsGramsFraction = new LinkedHashMap<>();
//		for (Map.Entry<Compound, Integer> entry : compounds.entrySet()){
//			Compound compound = entry.getKey();
//			int quantity = entry.getValue();
//			compoundsGramsFraction.put(compound, Fraction.fromDouble(compound.getMolarMass()).multiplySelfBy(quantity));
//		}
//		return compoundsGramsFraction;
////        int lowestCommonDenominator = Fraction.lowestCommonDenominator(compoundsGramsFraction.values());
////
////		Map<Compound, Integer> compoundsGrams = new LinkedHashMap<>();
////		for (Map.Entry<Compound, Fraction> entry : compoundsGramsFraction.entrySet()){
////			Compound compound = entry.getKey();
////			Fraction quantity = entry.getValue();
////			compoundsGrams.put(compound, quantity.multiplySelfBy(lowestCommonDenominator).getNumerator());
////		}
////		return compoundsGrams;
//	}
	
//	private static Map<Compound, Integer> multiplyCompoundsByLCD(Map<Compound, Fraction> compounds, int lcd){
//		Map<Compound, Integer> compoundsScaled = new LinkedHashMap<>();
//		for (Map.Entry<Compound, Fraction> entry : compounds.entrySet()){
//			Compound compound = entry.getKey();
//			Fraction quantity = entry.getValue();
//			compoundsScaled.put(compound, quantity.multiplySelfBy(lcd).getNumerator());
//		}
//		return compoundsScaled;
//	}
	
//	public Reaction toGrams() {
//		Map<Compound, Fraction> reactantsFraction = toGrams(this.reactants);
//		Map<Compound, Fraction> productsFraction = toGrams(this.products);
//		
////		System.out.println(reactantsFraction + " => " + productsFraction);
//		
//		Map<Compound, Fraction> all = new HashMap<>();
//		all.putAll(reactantsFraction);
//		all.putAll(productsFraction);
//		int lcd = Fraction.lowestCommonDenominator(all.values());
////		System.out.println(lcd);
//		
//		Map<Compound, Integer> reactants = multiplyCompoundsByLCD(reactantsFraction, lcd);
//		Map<Compound, Integer> products = multiplyCompoundsByLCD(productsFraction, lcd);
//		
//		return new Reaction(reactants, products, true);
//	}

	public static void printMolarMasses(Map<Compound, Integer> compounds) {
		for (Map.Entry<Compound, Integer> entry: compounds.entrySet()){
			Compound c = entry.getKey();
			System.out.print(Fraction.round8(c.getMolarMass()) + "     ");
		}
	}
	public static void printDoubles(Map<Compound, Double> compounds) {
		for (Map.Entry<Compound, Double> entry: compounds.entrySet()){
//			Compound c = entry.getKey();
			System.out.print(entry.getValue() + "         ");
		}
	}
	
	public void printInfo() {
		System.out.println(
				new ReactionStringTableBuilder()
				.add().buildCompounds(reactants).add(" => ").buildCompounds(products).newRow()
				.add("g/mol").buildMolarMasses(reactants).add().buildMolarMasses(products).newRow()
				.add("mol").buildMoles(reactants).add().buildMoles(products).newRow()
				.add("g").buildGrams(reactants).add().buildGrams(products)
				.build(2, Align.CENTER)
				);
	}

	public int getCoefficient(Compound c){
		Integer coefficient = reactants.getCoefficient(c);
		if (coefficient == null){
			coefficient = products.getCoefficient(c);
		}
		if (coefficient == null) {
			throw new InvalidKeyException("Compound \""+c+"\" not contained in the reaction.");
		}
		return coefficient;
	}
	
	public Reaction setMoles(String s, double moles){
		return setMoles(Compound.fromString(s), moles);
	}

	public Reaction setGrams(String s, double grams){
		return setGrams(Compound.fromString(s), grams);
	}
	
	public Compound getAtIndex(int index){
		if(index < reactants.size()){
			return reactants.getAtIndex(index);
		} else if(index-reactants.size() < products.size()){
			return products.getAtIndex(index-reactants.size()); 
		} else {
			return null;
		}
	}
	
	public Reaction setMoles(int i, int moles) {
		Compound compound = getAtIndex(i);
		if (compound == null){
			System.out.println("Index out of range.");
			return this;
		} else {
			return setMoles(compound, moles);
		}
	}

	public Reaction setGrams(int i, double grams) {
		Compound compound = getAtIndex(i);
		if (compound == null){
			System.out.println("Index out of range.");
			return this;
		} else {
			return setGrams(compound, grams);
		}
	}
	
	public Reaction setMoles(Compound compound, double moles){
		double molesPerCoefficient;
		try {
			molesPerCoefficient = moles / getCoefficient(compound);
		} catch (InvalidKeyException e) {
			System.out.println(e.getMessage());
			return this;
		}
		reactants.setMolesPerCoefficient(molesPerCoefficient);
		products.setMolesPerCoefficient(molesPerCoefficient);
		return this;
	}

	public Reaction setGrams(Compound compound, double grams){
		return setMoles(compound, grams / compound.getMolarMass());
	}

        //        
        //        int j = 0;
        //        for ( entry : reactants.entrySet()){
        //        	
        //        	j++;
        //        }
        //        for (int j=0; j<coefficients.size(); j++) {
        //        	if (j < reactants.size()) {
        //        		reactants.set();
        //        	} else {
        //        		
        //        	}
        //        }
        //System.out.println("Solved		" + reactants + " => " + products);
        return new Reaction(reactants, products); //, false);
    }

    private static List<Integer> negative(List<Integer> list) {
        List<Integer> negative = new ArrayList<>();
        for (int i : list) {
            negative.add(-i);
        }
        return negative;
    }

    //	private static List<Integer> countFactors(Element e, Map<Compound, Integer> compounds){
    //		List<Integer> factors = new ArrayList<>();
    //		for (Map.Entry<Compound, Integer> entry : compounds.entrySet()){
    //			Compound compound = entry.getKey();
    //			if (compound.getElements().containsKey(e)){
    //				factors.add(compound.getElements().get(e));
    //			} else {
    //				factors.add(0);
    //			}
    //		}
    //		return factors;
    //	}

    public Expression getReactants() {
        return reactants;
    }

    public Expression getProducts() {
        return products;
    }

    public static String getCompoundsString(Map<Compound, Integer> compounds, boolean grams) {
        //TODO use a StringBuilder for Reaction.getCompoundsString()
        String s = "";
        boolean first = true;
        for (Map.Entry<Compound, Integer> entry : compounds.entrySet()) {
            Compound compound = entry.getKey();
            int quantity = entry.getValue();
            if (quantity != 0) {
                if (first) {
                    first = false;
                } else {
                    s += " + ";
                }
                if (quantity != 1 || grams) {
                    if (grams) {
                        s += quantity + "g ";
                    } else {
                        s += quantity + " ";
                    }
                }
                s += compound;
            }
        }
        return s;
    }

    @Override
    public String toString() {
        return reactants + " => " + products;
    }

    //	public static Map<Compound, Fraction> toGrams(Map<Compound, Integer> compounds){
    //		Map<Compound, Fraction> compoundsGramsFraction = new LinkedHashMap<>();
    //		for (Map.Entry<Compound, Integer> entry : compounds.entrySet()){
    //			Compound compound = entry.getKey();
    //			int quantity = entry.getValue();
    //			compoundsGramsFraction.put(compound, Fraction.fromDouble(compound.getMolarMass()).multiplySelfBy(quantity));
    //		}
    //		return compoundsGramsFraction;
    ////        int lowestCommonDenominator = Fraction.lowestCommonDenominator(compoundsGramsFraction.values());
    ////
    ////		Map<Compound, Integer> compoundsGrams = new LinkedHashMap<>();
    ////		for (Map.Entry<Compound, Fraction> entry : compoundsGramsFraction.entrySet()){
    ////			Compound compound = entry.getKey();
    ////			Fraction quantity = entry.getValue();
    ////			compoundsGrams.put(compound, quantity.multiplySelfBy(lowestCommonDenominator).getNumerator());
    ////		}
    ////		return compoundsGrams;
    //	}

    //	private static Map<Compound, Integer> multiplyCompoundsByLCD(Map<Compound, Fraction> compounds, int lcd){
    //		Map<Compound, Integer> compoundsScaled = new LinkedHashMap<>();
    //		for (Map.Entry<Compound, Fraction> entry : compounds.entrySet()){
    //			Compound compound = entry.getKey();
    //			Fraction quantity = entry.getValue();
    //			compoundsScaled.put(compound, quantity.multiplySelfBy(lcd).getNumerator());
    //		}
    //		return compoundsScaled;
    //	}

    //	public Reaction toGrams() {
    //		Map<Compound, Fraction> reactantsFraction = toGrams(this.reactants);
    //		Map<Compound, Fraction> productsFraction = toGrams(this.products);
    //		
    ////		System.out.println(reactantsFraction + " => " + productsFraction);
    //		
    //		Map<Compound, Fraction> all = new HashMap<>();
    //		all.putAll(reactantsFraction);
    //		all.putAll(productsFraction);
    //		int lcd = Fraction.lowestCommonDenominator(all.values());
    ////		System.out.println(lcd);
    //		
    //		Map<Compound, Integer> reactants = multiplyCompoundsByLCD(reactantsFraction, lcd);
    //		Map<Compound, Integer> products = multiplyCompoundsByLCD(productsFraction, lcd);
    //		
    //		return new Reaction(reactants, products, true);
    //	}

    public static void printMolarMasses(Map<Compound, Integer> compounds) {
        for (Map.Entry<Compound, Integer> entry : compounds.entrySet()) {
            Compound c = entry.getKey();
            System.out.print(Fraction.round8(c.getMolarMass()) + "     ");
        }
    }

    public static void printDoubles(Map<Compound, Double> compounds) {
        for (Map.Entry<Compound, Double> entry : compounds.entrySet()) {
            //			Compound c = entry.getKey();
            System.out.print(entry.getValue() + "         ");
        }
    }

    public void printInfo() {
        System.out.println(new ReactionStringTableBuilder().add().buildCompounds(reactants).add(" => ").buildCompounds(products).newRow().add("g/mol").buildMolarMasses(reactants).add().buildMolarMasses(products).newRow().add("mol").buildMoles(reactants).add().buildMoles(products).newRow().add("g").buildGrams(reactants).add().buildGrams(products).build(2, Align.CENTER));
    }

    public int getCoefficient(Compound c) {
        Integer coefficient = reactants.getCoefficient(c);
        if (coefficient == null) {
            coefficient = products.getCoefficient(c);
        }
        if (coefficient == null) {
            throw new InvalidKeyException("Compound \"" + c + "\" not contained in the reaction.");
        }
        return coefficient;
    }

    public Reaction setMoles(String s, double moles) {
        return setMoles(Compound.fromString(s), moles);
    }

    public Reaction setGrams(String s, double grams) {
        return setGrams(Compound.fromString(s), grams);
    }

    public Compound getAtIndex(int index) {
        if (index < reactants.size()) {
            return reactants.getAtIndex(index);
        } else if (index - reactants.size() < products.size()) {
            return products.getAtIndex(index - reactants.size());
        } else {
            return null;
        }
    }

    public Reaction setMoles(int i, int moles) {
        Compound compound = getAtIndex(i);
        if (compound == null) {
            System.out.println("Index out of range.");
            return this;
        } else {
            return setMoles(compound, moles);
        }
    }

    public Reaction setGrams(int i, double grams) {
        Compound compound = getAtIndex(i);
        if (compound == null) {
            System.out.println("Index out of range.");
            return this;
        } else {
            return setGrams(compound, grams);
        }
    }

    public Reaction setMoles(Compound compound, double moles) {
        double molesPerCoefficient;
        try {
            molesPerCoefficient = moles / getCoefficient(compound);
        } catch (InvalidKeyException e) {
            System.out.println(e.getMessage());
            return this;
        }
        reactants.setMolesPerCoefficient(molesPerCoefficient);
        products.setMolesPerCoefficient(molesPerCoefficient);
        return this;
    }

    public Reaction setGrams(Compound compound, double grams) {
        return setMoles(compound, grams / compound.getMolarMass());
    }
}