From 28c861db52484eefa37bd0ef795b9329aa8b0290 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Fri, 25 Jan 2019 12:30:52 -0500 Subject: Initial commit --- src/unitConverter/dimension/BaseDimension.java | 37 ++++ .../dimension/OtherBaseDimension.java | 53 +++++ src/unitConverter/dimension/SIBaseDimension.java | 55 +++++ .../dimension/StandardDimensions.java | 79 +++++++ src/unitConverter/dimension/UnitDimension.java | 230 +++++++++++++++++++++ src/unitConverter/dimension/UnitDimensionTest.java | 56 +++++ src/unitConverter/dimension/package-info.java | 23 +++ 7 files changed, 533 insertions(+) create mode 100755 src/unitConverter/dimension/BaseDimension.java create mode 100755 src/unitConverter/dimension/OtherBaseDimension.java create mode 100755 src/unitConverter/dimension/SIBaseDimension.java create mode 100755 src/unitConverter/dimension/StandardDimensions.java create mode 100755 src/unitConverter/dimension/UnitDimension.java create mode 100755 src/unitConverter/dimension/UnitDimensionTest.java create mode 100755 src/unitConverter/dimension/package-info.java (limited to 'src/unitConverter/dimension') diff --git a/src/unitConverter/dimension/BaseDimension.java b/src/unitConverter/dimension/BaseDimension.java new file mode 100755 index 0000000..6a727a9 --- /dev/null +++ b/src/unitConverter/dimension/BaseDimension.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package unitConverter.dimension; + +/** + * A base dimension that makes up {@code UnitDimension} objects. + * + * @author Adrien Hopkins + * @since 2018-12-22 + */ +public interface BaseDimension { + /** + * @return the dimension's name + * @since 2019-01-25 + */ + String getName(); + + /** + * @return a short string (usually one character) that represents this base dimension + * @since 2018-12-22 + */ + String getSymbol(); +} diff --git a/src/unitConverter/dimension/OtherBaseDimension.java b/src/unitConverter/dimension/OtherBaseDimension.java new file mode 100755 index 0000000..2ab19b3 --- /dev/null +++ b/src/unitConverter/dimension/OtherBaseDimension.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package unitConverter.dimension; + +import java.util.Objects; + +/** + * Non-SI base dimensions. + * + * @author Adrien Hopkins + * @since 2019-01-14 + */ +public enum OtherBaseDimension implements BaseDimension { + INFORMATION("Info"), CURRENCY("$$"); + + /** The dimension's symbol */ + private final String symbol; + + /** + * Creates the {@code SIBaseDimension}. + * + * @param symbol + * dimension's symbol + * @since 2018-12-11 + */ + private OtherBaseDimension(final String symbol) { + this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); + } + + @Override + public String getName() { + return this.toString(); + } + + @Override + public String getSymbol() { + return this.symbol; + } +} diff --git a/src/unitConverter/dimension/SIBaseDimension.java b/src/unitConverter/dimension/SIBaseDimension.java new file mode 100755 index 0000000..b731b00 --- /dev/null +++ b/src/unitConverter/dimension/SIBaseDimension.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package unitConverter.dimension; + +import java.util.Objects; + +/** + * The seven base dimensions that make up the SI. + * + * @author Adrien Hopkins + * @since 2018-12-11 + */ +public enum SIBaseDimension implements BaseDimension { + LENGTH("L"), MASS("M"), TIME("T"), ELECTRIC_CURRENT("I"), TEMPERATURE("\u0398"), // u0398 is the theta symbol + QUANTITY("N"), LUMINOUS_INTENSITY("J"); + + /** The dimension's symbol */ + private final String symbol; + + /** + * Creates the {@code SIBaseDimension}. + * + * @param symbol + * dimension's symbol + * @since 2018-12-11 + */ + private SIBaseDimension(final String symbol) { + this.symbol = Objects.requireNonNull(symbol, "symbol must not be null."); + } + + @Override + public String getName() { + return this.toString(); + } + + @Override + public String getSymbol() { + return this.symbol; + } + +} diff --git a/src/unitConverter/dimension/StandardDimensions.java b/src/unitConverter/dimension/StandardDimensions.java new file mode 100755 index 0000000..c830f00 --- /dev/null +++ b/src/unitConverter/dimension/StandardDimensions.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package unitConverter.dimension; + +/** + * All of the dimensions that are used by the SI. + * + * @author Adrien Hopkins + * @since 2018-12-11 + */ +public final class StandardDimensions { + // base dimensions + public static final UnitDimension EMPTY = UnitDimension.EMPTY; + public static final UnitDimension LENGTH = UnitDimension.getBase(SIBaseDimension.LENGTH); + public static final UnitDimension MASS = UnitDimension.getBase(SIBaseDimension.MASS); + public static final UnitDimension TIME = UnitDimension.getBase(SIBaseDimension.TIME); + public static final UnitDimension ELECTRIC_CURRENT = UnitDimension.getBase(SIBaseDimension.ELECTRIC_CURRENT); + public static final UnitDimension TEMPERATURE = UnitDimension.getBase(SIBaseDimension.TEMPERATURE); + public static final UnitDimension QUANTITY = UnitDimension.getBase(SIBaseDimension.QUANTITY); + public static final UnitDimension LUMINOUS_INTENSITY = UnitDimension.getBase(SIBaseDimension.LUMINOUS_INTENSITY); + public static final UnitDimension INFORMATION = UnitDimension.getBase(OtherBaseDimension.INFORMATION); + public static final UnitDimension CURRENCY = UnitDimension.getBase(OtherBaseDimension.CURRENCY); + // derived dimensions without named SI units + public static final UnitDimension AREA = LENGTH.times(LENGTH); + + public static final UnitDimension VOLUME = AREA.times(LENGTH); + public static final UnitDimension VELOCITY = LENGTH.dividedBy(TIME); + public static final UnitDimension ACCELERATION = VELOCITY.dividedBy(TIME); + public static final UnitDimension WAVENUMBER = EMPTY.dividedBy(LENGTH); + public static final UnitDimension MASS_DENSITY = MASS.dividedBy(VOLUME); + public static final UnitDimension SURFACE_DENSITY = MASS.dividedBy(AREA); + public static final UnitDimension SPECIFIC_VOLUME = VOLUME.dividedBy(MASS); + public static final UnitDimension CURRENT_DENSITY = ELECTRIC_CURRENT.dividedBy(AREA); + public static final UnitDimension MAGNETIC_FIELD_STRENGTH = ELECTRIC_CURRENT.dividedBy(LENGTH); + public static final UnitDimension CONCENTRATION = QUANTITY.dividedBy(VOLUME); + public static final UnitDimension MASS_CONCENTRATION = CONCENTRATION.times(MASS); + public static final UnitDimension LUMINANCE = LUMINOUS_INTENSITY.dividedBy(AREA); + public static final UnitDimension REFRACTIVE_INDEX = VELOCITY.dividedBy(VELOCITY); + public static final UnitDimension REFLACTIVE_PERMEABILITY = EMPTY.times(EMPTY); + public static final UnitDimension ANGLE = LENGTH.dividedBy(LENGTH); + public static final UnitDimension SOLID_ANGLE = AREA.dividedBy(AREA); + // derived dimensions with named SI units + public static final UnitDimension FREQUENCY = EMPTY.dividedBy(TIME); + + public static final UnitDimension FORCE = MASS.times(ACCELERATION); + public static final UnitDimension ENERGY = FORCE.times(LENGTH); + public static final UnitDimension POWER = ENERGY.dividedBy(TIME); + public static final UnitDimension ELECTRIC_CHARGE = ELECTRIC_CURRENT.times(TIME); + public static final UnitDimension VOLTAGE = ENERGY.dividedBy(ELECTRIC_CHARGE); + public static final UnitDimension CAPACITANCE = ELECTRIC_CHARGE.dividedBy(VOLTAGE); + public static final UnitDimension ELECTRIC_RESISTANCE = VOLTAGE.dividedBy(ELECTRIC_CURRENT); + public static final UnitDimension ELECTRIC_CONDUCTANCE = ELECTRIC_CURRENT.dividedBy(VOLTAGE); + public static final UnitDimension MAGNETIC_FLUX = VOLTAGE.times(TIME); + public static final UnitDimension MAGNETIC_FLUX_DENSITY = MAGNETIC_FLUX.dividedBy(AREA); + public static final UnitDimension INDUCTANCE = MAGNETIC_FLUX.dividedBy(ELECTRIC_CURRENT); + public static final UnitDimension LUMINOUS_FLUX = LUMINOUS_INTENSITY.times(SOLID_ANGLE); + public static final UnitDimension ILLUMINANCE = LUMINOUS_FLUX.dividedBy(AREA); + public static final UnitDimension SPECIFIC_ENERGY = ENERGY.dividedBy(MASS); + public static final UnitDimension CATALYTIC_ACTIVITY = QUANTITY.dividedBy(TIME); + + // You may NOT get StandardDimensions instances! + private StandardDimensions() { + throw new AssertionError(); + } +} diff --git a/src/unitConverter/dimension/UnitDimension.java b/src/unitConverter/dimension/UnitDimension.java new file mode 100755 index 0000000..ba2a750 --- /dev/null +++ b/src/unitConverter/dimension/UnitDimension.java @@ -0,0 +1,230 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package unitConverter.dimension; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * An object that represents what a unit measures, like length, mass, area, energy, etc. + * + * @author Adrien Hopkins + * @since 2018-12-11 + */ +public final class UnitDimension { + /** + * The unit dimension where every exponent is zero + * + * @since 2018-12-12 + */ + public static final UnitDimension EMPTY = new UnitDimension(new HashMap<>()); + + /** + * Gets an UnitDimension that has 1 of a certain dimension and nothing else + * + * @param dimension + * dimension to get + * @return unit dimension + * @since 2018-12-11 + */ + public static final UnitDimension getBase(final BaseDimension dimension) { + final Map map = new HashMap<>(); + map.put(dimension, 1); + return new UnitDimension(map); + } + + /** + * The base dimensions that make up this dimension. + * + * @since 2018-12-11 + */ + final Map exponents; + + /** + * Creates the {@code UnitDimension}. + * + * @param exponents + * base dimensions that make up this dimension + * @since 2018-12-11 + */ + private UnitDimension(final Map exponents) { + this.exponents = new HashMap<>(exponents); + } + + /** + * Divides this dimension by another + * + * @param other + * other dimension + * @return quotient of two dimensions + * @since 2018-12-11 + */ + public UnitDimension dividedBy(final UnitDimension other) { + final Map map = new HashMap<>(this.exponents); + + for (final BaseDimension key : other.exponents.keySet()) { + if (map.containsKey(key)) { + // add the dimensions + map.put(key, map.get(key) - other.exponents.get(key)); + } else { + map.put(key, -other.exponents.get(key)); + } + } + return new UnitDimension(map); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof UnitDimension)) + return false; + final UnitDimension other = (UnitDimension) obj; + + // anything with a value of 0 is equal to a nonexistent value + for (final BaseDimension b : this.getBaseSet()) { + if (this.exponents.get(b) != other.exponents.get(b)) + if (!(this.exponents.get(b) == 0 && !other.exponents.containsKey(b))) + return false; + } + for (final BaseDimension b : other.getBaseSet()) { + if (this.exponents.get(b) != other.exponents.get(b)) + if (!(other.exponents.get(b) == 0 && !this.exponents.containsKey(b))) + return false; + } + return true; + } + + /** + * @return a set of all of the base dimensions with non-zero exponents that make up this dimension. + * @since 2018-12-12 + */ + public final Set getBaseSet() { + final Set dimensions = new HashSet<>(); + + // add all dimensions with a nonzero exponent - they shouldn't be there in the first place + for (final BaseDimension dimension : this.exponents.keySet()) { + if (!this.exponents.get(dimension).equals(0)) { + dimensions.add(dimension); + } + } + + return dimensions; + } + + /** + * Gets the exponent for a specific dimension. + * + * @param dimension + * dimension to check + * @return exponent for that dimension + * @since 2018-12-12 + */ + public int getExponent(final BaseDimension dimension) { + return this.exponents.getOrDefault(dimension, 0); + } + + @Override + public int hashCode() { + return Objects.hash(this.exponents); + } + + /** + * @return true if this dimension is a base, i.e. it has one exponent of one and no other nonzero exponents + * @since 2019-01-15 + */ + public boolean isBase() { + int oneCount = 0; + boolean twoOrMore = false; // has exponents of 2 or more + for (final BaseDimension b : this.getBaseSet()) { + if (this.exponents.get(b) == 1) { + oneCount++; + } else if (this.exponents.get(b) != 0) { + twoOrMore = true; + } + } + return (oneCount == 0 || oneCount == 1) && !twoOrMore; + } + + /** + * Multiplies this dimension by another + * + * @param other + * other dimension + * @return product of two dimensions + * @since 2018-12-11 + */ + public UnitDimension times(final UnitDimension other) { + final Map map = new HashMap<>(this.exponents); + + for (final BaseDimension key : other.exponents.keySet()) { + if (map.containsKey(key)) { + // add the dimensions + map.put(key, map.get(key) + other.exponents.get(key)); + } else { + map.put(key, other.exponents.get(key)); + } + } + return new UnitDimension(map); + } + + /** + * Returns this dimension, but to an exponent + * + * @param exp + * exponent + * @return result of exponientation + * @since 2019-01-15 + */ + public UnitDimension toExponent(final int exp) { + final Map map = new HashMap<>(this.exponents); + for (final BaseDimension key : this.exponents.keySet()) { + map.put(key, this.getExponent(key) * exp); + } + return new UnitDimension(map); + } + + @Override + public String toString() { + final List positiveStringComponents = new ArrayList<>(); + final List negativeStringComponents = new ArrayList<>(); + + // for each base dimension that makes up this dimension, add it and its exponent + for (final BaseDimension dimension : this.getBaseSet()) { + final int exponent = this.exponents.get(dimension); + if (exponent > 0) { + positiveStringComponents.add(String.format("%s^%d", dimension.getSymbol(), exponent)); + } else if (exponent < 0) { + negativeStringComponents.add(String.format("%s^%d", dimension.getSymbol(), -exponent)); + } + } + + final String positiveString = positiveStringComponents.isEmpty() ? "1" + : String.join(" ", positiveStringComponents); + final String negativeString = negativeStringComponents.isEmpty() ? "" + : " / " + String.join(" ", negativeStringComponents); + + return positiveString + negativeString; + } +} diff --git a/src/unitConverter/dimension/UnitDimensionTest.java b/src/unitConverter/dimension/UnitDimensionTest.java new file mode 100755 index 0000000..603320b --- /dev/null +++ b/src/unitConverter/dimension/UnitDimensionTest.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package unitConverter.dimension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static unitConverter.dimension.StandardDimensions.AREA; +import static unitConverter.dimension.StandardDimensions.ENERGY; +import static unitConverter.dimension.StandardDimensions.LENGTH; +import static unitConverter.dimension.StandardDimensions.MASS; +import static unitConverter.dimension.StandardDimensions.MASS_DENSITY; +import static unitConverter.dimension.StandardDimensions.QUANTITY; +import static unitConverter.dimension.StandardDimensions.TIME; +import static unitConverter.dimension.StandardDimensions.VOLUME; + +import org.junit.jupiter.api.Test; + +/** + * @author Adrien Hopkins + * @since 2018-12-12 + */ +class UnitDimensionTest { + @Test + void testEquals() { + assertEquals(LENGTH, LENGTH); + assertFalse(LENGTH.equals(QUANTITY)); + } + + @Test + void testExponents() { + assertEquals(1, LENGTH.getExponent(SIBaseDimension.LENGTH)); + assertEquals(3, VOLUME.getExponent(SIBaseDimension.LENGTH)); + } + + @Test + void testMultiplicationAndDivision() { + assertEquals(AREA, LENGTH.times(LENGTH)); + assertEquals(MASS_DENSITY, MASS.dividedBy(VOLUME)); + assertEquals(ENERGY, AREA.times(MASS).dividedBy(TIME).dividedBy(TIME)); + assertEquals(LENGTH, LENGTH.times(TIME).dividedBy(TIME)); + } +} diff --git a/src/unitConverter/dimension/package-info.java b/src/unitConverter/dimension/package-info.java new file mode 100755 index 0000000..74895ce --- /dev/null +++ b/src/unitConverter/dimension/package-info.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2018 Adrien Hopkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +/** + * Everything to do with what a unit measures, or its dimension. + * + * @author Adrien Hopkins + * @since 2018-12-22 + */ +package unitConverter.dimension; \ No newline at end of file -- cgit v1.2.3