From cb0dd8e9926e1c3b26010f29331d187bb51ed246 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 4 Mar 2014 15:31:33 +0400 Subject: [PATCH] Adds java.lang.Math and java.util.HashSet --- .../java/lang/MathNativeGenerator.java | 59 +++++ .../org/teavm/classlib/java/lang/TMath.java | 208 ++++++++++++++++++ .../teavm/classlib/java/util/THashSet.java | 182 +++++++++++++++ .../teavm/classlib/java/lang/MathTest.java | 57 +++++ 4 files changed, 506 insertions(+) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/lang/MathTest.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java new file mode 100644 index 000000000..070f72d00 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/MathNativeGenerator.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang; + +import java.io.IOException; +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class MathNativeGenerator implements Generator { + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "sin": + case "cos": + case "tan": + case "asin": + case "acos": + case "atan": + case "exp": + case "log": + case "sqrt": + case "floor": + case "ceil": + function(context, writer, "Math." + methodRef.getName(), methodRef.parameterCount()); + break; + } + } + + private void function(GeneratorContext context, SourceWriter writer, String name, int paramCount) + throws IOException { + writer.append("return ").append(name).append("("); + for (int i = 0; i < paramCount; ++i) { + if (i > 0) { + writer.append(",").ws(); + } + writer.append(context.getParameterName(i + 1)); + } + writer.append(");").softNewLine(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index 5eedf399a..2fc765c26 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -15,14 +15,94 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.javascript.ni.GeneratedBy; + /** * * @author Alexey Andreev */ public final class TMath extends TObject { + public static double E = 2.71828182845904523536; + public static double PI = 3.14159265358979323846; + private TMath() { } + @GeneratedBy(MathNativeGenerator.class) + public static native double sin(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double cos(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double tan(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double asin(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double acos(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double atan(double a); + + public static double toRadians(double angdeg) { + return angdeg * PI / 180; + } + + public static double toDegrees(double angrad) { + return angrad * 180 / PI; + } + + @GeneratedBy(MathNativeGenerator.class) + public static native double exp(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double log(double a); + + public static double log10(double a) { + return log(a) / 2.302585092994046 /* log_e 10 */; + } + + @GeneratedBy(MathNativeGenerator.class) + public static native double sqrt(double a); + + public static double cbrt(double a) { + return a > 0 ? pow(a, 1.0 / 3) : -pow(-a, 1.0 / 3); + } + + public static double IEEEremainder(double f1, double f2) { + int n = (int)(f1 / f2); + return f1 - n * f2; + } + + @GeneratedBy(MathNativeGenerator.class) + public static native double ceil(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double floor(double a); + + @GeneratedBy(MathNativeGenerator.class) + public static native double pow(double x, double y); + + public static double rint(double a) { + return round(a); + } + + @GeneratedBy(MathNativeGenerator.class) + public static native double atan2(double y, double x); + + public static int round(float a) { + return (int)(a + 1.5f); + } + + public static long round(double a) { + return (long)(a + 0.5); + } + + @GeneratedBy(MathNativeGenerator.class) + public static native double random(); + public static int min(int a, int b) { return a < b ? a : b; } @@ -47,6 +127,14 @@ public final class TMath extends TObject { return a > b ? a : b; } + public static float min(float a, float b) { + return a < b ? a : b; + } + + public static float max(float a, float b) { + return a > b ? a : b; + } + public static int abs(int n) { return n > 0 ? n : -n; } @@ -55,7 +143,127 @@ public final class TMath extends TObject { return n > 0 ? n : -n; } + public static float abs(float n) { + return n > 0 ? n : -n; + } + public static double abs(double n) { return n > 0 ? n : -n; } + + public static double ulp(double d) { + return pow(1, -getExponent(d) - 52); + } + + public static float ulp(float d) { + return (float)pow(1, -getExponent(d) - 23); + } + + public static double signum(double d) { + return d > 0 ? 1 : d < -0 ? -1 : d; + } + + public static float signum(float d) { + return d > 0 ? 1 : d < -0 ? -1 : d; + } + + public static double sinh(double x) { + double e = exp(x); + return e - 1 / e; + } + + public static double cosh(double x) { + double e = exp(x); + return e + 1 / e; + } + + public static double tanh(double x) { + double e = exp(x); + return (e - 1 / e) / (e + 1 / e); + } + + public static double hypot(double x, double y) { + return x * x + y * y; + } + + public static double expm1(double x) { + return exp(x) - 1; + } + + public static double log1p(double x) { + return log(x + 1); + } + + public static float copySign(float magnitude, float sign) { + if (sign == 0 || sign == -0) { + return sign; + } + return (sign > 0) == (magnitude > 0) ? magnitude : -magnitude; + } + + public static double copySign(double magnitude, double sign) { + if (sign == 0 || sign == -0) { + return sign; + } + return (sign > 0) == (magnitude > 0) ? magnitude : -magnitude; + } + + public static int getExponent(double d) { + d = abs(d); + int exp = 0; + double[] exponents = ExponentConstants.exponents; + double[] negativeExponents = ExponentConstants.negativeExponents; + if (d > 1) { + int expBit = 1 << (exponents.length - 1); + for (int i = exponents.length - 1; i >= 0; --i) { + if (d > exponents[i]) { + d *= negativeExponents[i]; + exp |= expBit; + } + expBit >>>= 1; + } + } else if (d < 1) { + int expBit = 1 << (negativeExponents.length - 1); + for (int i = negativeExponents.length - 1; i >= 0; --i) { + if (d < negativeExponents[i]) { + d *= exponents[i]; + exp |= expBit; + } + expBit >>>= 1; + } + exp = -exp; + } + return exp; + } + + public static int getExponent(float f) { + return getExponent(f); + } + + public static double nextAfter(double start, double direction) { + if (start == direction) { + return direction; + } + return direction > start ? start + ulp(start) : start - ulp(start); + } + + public static float nextAfter(float start, float direction) { + if (start == direction) { + return direction; + } + return direction > start ? start + ulp(start) : start - ulp(start); + } + + public static double nextUp(double d) { + return d + ulp(d); + } + + public static float nextUp(float d) { + return d + ulp(d); + } + + private static class ExponentConstants { + public static double[] exponents = { 1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256 }; + public static double[] negativeExponents = { 1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32, 1E-64, 1E-128, 1E-256 }; + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java new file mode 100644 index 000000000..caeb35359 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java @@ -0,0 +1,182 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.util; + +import org.teavm.classlib.java.io.TSerializable; +import org.teavm.classlib.java.lang.TCloneNotSupportedException; +import org.teavm.classlib.java.lang.TCloneable; +import org.teavm.classlib.java.lang.TObject; + +/** + * + * @author Alexey Andreev + */ +public class THashSet extends TAbstractSet implements TCloneable, TSerializable { + transient THashMap> backingMap; + + /** + * Constructs a new empty instance of {@code HashSet}. + */ + public THashSet() { + this(new THashMap>()); + } + + /** + * Constructs a new instance of {@code HashSet} with the specified capacity. + * + * @param capacity + * the initial capacity of this {@code HashSet}. + */ + public THashSet(int capacity) { + this(new THashMap>(capacity)); + } + + /** + * Constructs a new instance of {@code HashSet} with the specified capacity + * and load factor. + * + * @param capacity + * the initial capacity. + * @param loadFactor + * the initial load factor. + */ + public THashSet(int capacity, float loadFactor) { + this(new THashMap>(capacity, loadFactor)); + } + + /** + * Constructs a new instance of {@code HashSet} containing the unique + * elements in the specified collection. + * + * @param collection + * the collection of elements to add. + */ + public THashSet(TCollection collection) { + this(new THashMap>(collection.size() < 6 ? 11 : collection.size() * 2)); + for (TIterator iter = collection.iterator(); iter.hasNext();) { + add(iter.next()); + } + } + + THashSet(THashMap> backingMap) { + this.backingMap = backingMap; + } + + /** + * Adds the specified object to this {@code HashSet} if not already present. + * + * @param object + * the object to add. + * @return {@code true} when this {@code HashSet} did not already contain + * the object, {@code false} otherwise + */ + @Override + public boolean add(E object) { + return backingMap.put(object, this) == null; + } + + /** + * Removes all elements from this {@code HashSet}, leaving it empty. + * + * @see #isEmpty + * @see #size + */ + @Override + public void clear() { + backingMap.clear(); + } + + /** + * Returns a new {@code HashSet} with the same elements and size as this + * {@code HashSet}. + * + * @return a shallow copy of this {@code HashSet}. + * @see java.lang.Cloneable + */ + @Override + @SuppressWarnings("unchecked") + public TObject clone() { + try { + THashSet clone = (THashSet) super.clone(); + clone.backingMap = (THashMap>)backingMap.clone(); + return clone; + } catch (TCloneNotSupportedException e) { + return null; + } + } + + /** + * Searches this {@code HashSet} for the specified object. + * + * @param object + * the object to search for. + * @return {@code true} if {@code object} is an element of this + * {@code HashSet}, {@code false} otherwise. + */ + @Override + public boolean contains(Object object) { + return backingMap.containsKey(object); + } + + /** + * Returns true if this {@code HashSet} has no elements, false otherwise. + * + * @return {@code true} if this {@code HashSet} has no elements, + * {@code false} otherwise. + * @see #size + */ + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + /** + * Returns an Iterator on the elements of this {@code HashSet}. + * + * @return an Iterator on the elements of this {@code HashSet}. + * @see Iterator + */ + @Override + public TIterator iterator() { + return backingMap.keySet().iterator(); + } + + /** + * Removes the specified object from this {@code HashSet}. + * + * @param object + * the object to remove. + * @return {@code true} if the object was removed, {@code false} otherwise. + */ + @Override + public boolean remove(Object object) { + return backingMap.remove(object) != null; + } + + /** + * Returns the number of elements in this {@code HashSet}. + * + * @return the number of elements in this {@code HashSet}. + */ + @Override + public int size() { + return backingMap.size(); + } + + THashMap> createBackingMap(int capacity, float loadFactor) { + return new THashMap<>(capacity, loadFactor); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/MathTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/MathTest.java new file mode 100644 index 000000000..471523b8c --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/MathTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang; + +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class MathTest { + @Test + public void sinComputed() { + assertEquals(0.90929742682568, Math.sin(2), 1E-14); + } + + @Test + public void expComputed() { + assertEquals(3.4212295362896734, Math.exp(1.23), 1E-14); + } + + @Test + public void cbrtComputed() { + assertEquals(3.0, Math.cbrt(27.0), 1E-14); + assertEquals(-3.0, Math.cbrt(-27.0), 1E-14); + assertEquals(0, Math.cbrt(0), 1E-14); + } + + @Test + public void ulpComputed() { + assertEquals(1.4210854715202004e-14, Math.ulp(123.456), 1E-25); + } + + @Test + public void sinhComputed() { + assertEquals(1.3097586593745313E53, Math.sinh(123), 1E40); + } + + @Test + public void getExponentComputed() { + assertEquals(6, Math.getExponent(123.456)); + } +}