| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.EOFException; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Properties; |
| |
| import org.apache.commons.math3.stat.descriptive.SummaryStatistics; |
| import org.apache.commons.math3.util.FastMath; |
| |
| /* |
| * plot 'logGamma.dat' binary format="%double%double" endian=big u 1:2 w l |
| */ |
| public class RealFunctionValidation { |
| |
| public static class MissingRequiredPropertyException |
| extends IllegalArgumentException { |
| |
| private static final long serialVersionUID = 20121017L; |
| |
| public MissingRequiredPropertyException(final String key) { |
| |
| super("missing required property " + key); |
| } |
| } |
| |
| public static class ApplicationProperties { |
| |
| private static final int DOT = '.'; |
| |
| private static final String METHOD_KEY = "method"; |
| |
| private static final String SIGNATURE_KEY = "signature"; |
| |
| private static final String INPUT_FILE_MASK = "inputFileMask"; |
| |
| private static final String OUTPUT_FILE_MASK = "outputFileMask"; |
| |
| private static final String FROM_KEY = "from"; |
| |
| private static final String TO_KEY = "to"; |
| |
| private static final String BY_KEY = "by"; |
| |
| final Method method; |
| |
| final String inputFileMask; |
| |
| final String outputFileMask; |
| |
| final int from; |
| |
| final int to; |
| |
| final int by; |
| |
| /** |
| * Returns a {@link Method} with specified signature. |
| * |
| * @param className The fully qualified name of the class to which the |
| * method belongs. |
| * @param methodName The name of the method. |
| * @param signature The signature of the method, as a list of parameter |
| * types. |
| * @return the method |
| * @throws SecurityException |
| * @throws ClassNotFoundException |
| */ |
| public static Method findStaticMethod(final String className, |
| final String methodName, |
| final List<Class<?>> signature) |
| throws SecurityException, ClassNotFoundException { |
| |
| final int n = signature.size(); |
| final Method[] methods = Class.forName(className).getMethods(); |
| for (Method method : methods) { |
| if (method.getName().equals(methodName)) { |
| final Class<?>[] parameters = method.getParameterTypes(); |
| boolean sameSignature = true; |
| if (parameters.length == n) { |
| for (int i = 0; i < n; i++) { |
| sameSignature &= signature.get(i) |
| .equals(parameters[i]); |
| } |
| if (sameSignature) { |
| final int modifiers = method.getModifiers(); |
| if ((modifiers & Modifier.STATIC) != 0) { |
| return method; |
| } else { |
| final String msg = "method must be static"; |
| throw new IllegalArgumentException(msg); |
| } |
| } |
| } |
| } |
| } |
| throw new IllegalArgumentException("method not found"); |
| } |
| |
| public static Class<?> parsePrimitiveType(final String type) { |
| |
| if (type.equals("boolean")) { |
| return Boolean.TYPE; |
| } else if (type.equals("byte")) { |
| return Byte.TYPE; |
| } else if (type.equals("char")) { |
| return Character.TYPE; |
| } else if (type.equals("double")) { |
| return Double.TYPE; |
| } else if (type.equals("float")) { |
| return Float.TYPE; |
| } else if (type.equals("int")) { |
| return Integer.TYPE; |
| } else if (type.equals("long")) { |
| return Long.TYPE; |
| } else if (type.equals("short")) { |
| return Short.TYPE; |
| } else { |
| final StringBuilder builder = new StringBuilder(); |
| builder.append(type).append(" is not a primitive type"); |
| throw new IllegalArgumentException(builder.toString()); |
| } |
| } |
| |
| private static String getPropertyAsString(final Properties properties, |
| final String key) { |
| |
| final String value = properties.getProperty(key); |
| if (value == null) { |
| throw new MissingRequiredPropertyException(key); |
| } else { |
| return value; |
| } |
| } |
| |
| private static int getPropertyAsInteger(final Properties properties, |
| final String key) { |
| |
| final String value = properties.getProperty(key); |
| if (value == null) { |
| throw new MissingRequiredPropertyException(key); |
| } else { |
| return Integer.parseInt(value); |
| } |
| } |
| |
| private ApplicationProperties(final String fullyQualifiedName, |
| final String signature, |
| final String inputFileMask, |
| final String outputFileMask, |
| final int from, final int to, final int by) { |
| |
| this.inputFileMask = inputFileMask; |
| this.outputFileMask = outputFileMask; |
| this.from = from; |
| this.to = to; |
| this.by = by; |
| |
| final String[] types = signature.split(","); |
| final List<Class<?>> parameterTypes = new ArrayList<Class<?>>(); |
| for (String type : types) { |
| parameterTypes.add(parsePrimitiveType(type.trim())); |
| } |
| final int index = fullyQualifiedName.lastIndexOf(DOT); |
| try { |
| final String className, methodName; |
| className = fullyQualifiedName.substring(0, index); |
| methodName = fullyQualifiedName.substring(index + 1); |
| this.method = findStaticMethod(className, methodName, |
| parameterTypes); |
| } catch (ClassNotFoundException e) { |
| throw new IllegalArgumentException(e); |
| } |
| |
| } |
| |
| public static final ApplicationProperties create(final Properties properties) { |
| |
| final String methodFullyQualifiedName; |
| methodFullyQualifiedName = getPropertyAsString(properties, |
| METHOD_KEY); |
| |
| final String signature; |
| signature = getPropertyAsString(properties, SIGNATURE_KEY); |
| |
| final String inputFileMask; |
| inputFileMask = getPropertyAsString(properties, INPUT_FILE_MASK); |
| |
| final String outputFileMask; |
| outputFileMask = getPropertyAsString(properties, OUTPUT_FILE_MASK); |
| |
| final int from = getPropertyAsInteger(properties, FROM_KEY); |
| final int to = getPropertyAsInteger(properties, TO_KEY); |
| final int by = getPropertyAsInteger(properties, BY_KEY); |
| |
| return new ApplicationProperties(methodFullyQualifiedName, |
| signature, inputFileMask, |
| outputFileMask, from, to, by); |
| } |
| }; |
| |
| public static Object readAndWritePrimitiveValue(final DataInputStream in, |
| final DataOutputStream out, |
| final Class<?> type) |
| throws IOException { |
| |
| if (!type.isPrimitive()) { |
| throw new IllegalArgumentException("type must be primitive"); |
| } |
| if (type.equals(Boolean.TYPE)) { |
| final boolean x = in.readBoolean(); |
| out.writeBoolean(x); |
| return Boolean.valueOf(x); |
| } else if (type.equals(Byte.TYPE)) { |
| final byte x = in.readByte(); |
| out.writeByte(x); |
| return Byte.valueOf(x); |
| } else if (type.equals(Character.TYPE)) { |
| final char x = in.readChar(); |
| out.writeChar(x); |
| return Character.valueOf(x); |
| } else if (type.equals(Double.TYPE)) { |
| final double x = in.readDouble(); |
| out.writeDouble(x); |
| return Double.valueOf(x); |
| } else if (type.equals(Float.TYPE)) { |
| final float x = in.readFloat(); |
| out.writeFloat(x); |
| return Float.valueOf(x); |
| } else if (type.equals(Integer.TYPE)) { |
| final int x = in.readInt(); |
| out.writeInt(x); |
| return Integer.valueOf(x); |
| } else if (type.equals(Long.TYPE)) { |
| final long x = in.readLong(); |
| out.writeLong(x); |
| return Long.valueOf(x); |
| } else if (type.equals(Short.TYPE)) { |
| final short x = in.readShort(); |
| out.writeShort(x); |
| return Short.valueOf(x); |
| } else { |
| // This should never occur. |
| throw new IllegalStateException(); |
| } |
| } |
| |
| public static SummaryStatistics assessAccuracy(final Method method, |
| final DataInputStream in, |
| final DataOutputStream out) |
| throws IOException, IllegalAccessException, IllegalArgumentException, |
| InvocationTargetException { |
| |
| if (method.getReturnType() != Double.TYPE) { |
| throw new IllegalArgumentException("method must return a double"); |
| } |
| |
| final Class<?>[] types = method.getParameterTypes(); |
| for (int i = 0; i < types.length; i++) { |
| if (!types[i].isPrimitive()) { |
| final StringBuilder builder = new StringBuilder(); |
| builder.append("argument #").append(i + 1) |
| .append(" of method ").append(method.getName()) |
| .append("must be of primitive of type"); |
| throw new IllegalArgumentException(builder.toString()); |
| } |
| } |
| |
| final SummaryStatistics stat = new SummaryStatistics(); |
| final Object[] parameters = new Object[types.length]; |
| while (true) { |
| try { |
| for (int i = 0; i < parameters.length; i++) { |
| parameters[i] = readAndWritePrimitiveValue(in, out, |
| types[i]); |
| } |
| final double expected = in.readDouble(); |
| if (FastMath.abs(expected) > 1E-16) { |
| final Object value = method.invoke(null, parameters); |
| final double actual = ((Double) value).doubleValue(); |
| final double err = FastMath.abs(actual - expected); |
| final double ulps = err / FastMath.ulp(expected); |
| out.writeDouble(expected); |
| out.writeDouble(actual); |
| out.writeDouble(ulps); |
| stat.addValue(ulps); |
| } |
| } catch (EOFException e) { |
| break; |
| } |
| } |
| return stat; |
| } |
| |
| public static void run(final ApplicationProperties properties) |
| throws IllegalAccessException, IllegalArgumentException, |
| InvocationTargetException, IOException { |
| |
| for (int i = properties.from; i < properties.to; i += properties.by) { |
| final String inputFileName; |
| inputFileName = String.format(properties.inputFileMask, i); |
| final String outputFileName; |
| outputFileName = String.format(properties.outputFileMask, i); |
| |
| final DataInputStream in; |
| in = new DataInputStream(new FileInputStream(inputFileName)); |
| final DataOutputStream out; |
| out = new DataOutputStream(new FileOutputStream(outputFileName)); |
| |
| final SummaryStatistics stats; |
| stats = assessAccuracy(properties.method, in, out); |
| |
| System.out.println("input file name = " + inputFileName); |
| System.out.println("output file name = " + outputFileName); |
| System.out.println(stats); |
| } |
| } |
| |
| public static void main(final String[] args) |
| throws IOException, IllegalAccessException, IllegalArgumentException, |
| InvocationTargetException { |
| |
| if (args.length == 0) { |
| final String msg = "missing required properties file"; |
| throw new IllegalArgumentException(msg); |
| } |
| |
| final FileInputStream in = new FileInputStream(args[0]); |
| final Properties properties = new Properties(); |
| properties.load(in); |
| in.close(); |
| |
| final ApplicationProperties p; |
| p = ApplicationProperties.create(properties); |
| |
| run(p); |
| } |
| } |