Skip to content

Commit

Permalink
Merge branch 'primitives'
Browse files Browse the repository at this point in the history
This adds support for all of Java's built-in unary and binary operators
on primitive types, as well as nearly all the Math.* unary and binary
functions which operate on primitive. It also closes #106.

This is an important step toward having an extensible expression parser
which works for numeric types as expected, but is not limited to them.
  • Loading branch information
ctrueden committed Jan 30, 2015
2 parents c51ce63 + bd06039 commit af05aff
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 54 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@
</pluginExecutionFilter>
<action>
<execute>
<runOnConfiguration>true</runOnConfiguration>
<runOnIncremental>false</runOnIncremental>
</execute>
</action>
Expand Down
21 changes: 20 additions & 1 deletion src/main/groovy/generate.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@
templateDirectory = project.properties['templateDirectory']
outputDirectory = project.properties['outputDirectory']

knownFiles = new java.util.HashSet();

/* Gets the last modified timestap for the given file. */
def timestamp(dir, file) {
if (file == null) return Long.MAX_VALUE;
return new java.io.File(dir, file).lastModified();
file = new java.io.File(dir, file);
knownFiles.add(file);
return file.lastModified();
}

/* Processes a template using Apache Velocity. */
Expand All @@ -43,6 +47,7 @@ def processTemplate(engine, context, templateFile, outFilename) {

// create output directory if it does not already exist
outFile = new java.io.File(outputDirectory, outFilename);
knownFiles.add(outFile);
if (outFile.getParentFile() != null) outFile.getParentFile().mkdirs();

// apply the template and write out the result
Expand Down Expand Up @@ -188,8 +193,22 @@ def translateDirectory(templateSubdirectory) {
}
}

def cleanStaleFiles(directory) {
list = directory == null ? null : directory.listFiles();
if (list == null) return;

for (File file : list) {
if (file.isDirectory()) {
cleanStaleFiles(file);
} else if (file.isFile() && !knownFiles.contains(file)) {
file.delete();
}
}
}

try {
translateDirectory(templateDirectory);
cleanStaleFiles(new File(outputDirectory));
}
catch (Throwable t) {
t.printStackTrace(System.err);
Expand Down
9 changes: 4 additions & 5 deletions src/main/templates/net/imagej/ops/Ops.list
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ ops = ```
[name: "createimg", iface: "CreateImg"],
[name: "crop", iface: "Crop", aliases: ["slice"]],
[name: "deconvolve", iface: "Deconvolve"],
[name: "divide", iface: "Divide"],
[name: "equation", iface: "Equation"],
[name: "fft", iface: "FFT"],
[name: "fftsize", iface: "FFTSize"],
Expand Down Expand Up @@ -51,7 +50,7 @@ authors = ["Jonathan Hale", "Christian Dietz", "Curtis Rueden"]
ops = ```
[
[name: "abs", iface: "Abs"],
[name: "add", iface: "Add"],
[name: "add", iface: "Add", aliases: ["sum"]],
[name: "addnoise", iface: "AddNoise"],
[name: "and", iface: "And"],
[name: "andconstant", iface: "AndConstant"],
Expand All @@ -77,7 +76,7 @@ ops = ```
[name: "csc", iface: "Csc"],
[name: "csch", iface: "Csch"],
[name: "cuberoot", iface: "CubeRoot"],
[name: "divide", iface: "Divide", aliases: ["div"]],
[name: "divide", iface: "Divide", aliases: ["div", "quotient"]],
[name: "exp", iface: "Exp"],
[name: "expminusone", iface: "ExpMinusOne"],
[name: "floor", iface: "Floor"],
Expand All @@ -91,7 +90,7 @@ ops = ```
[name: "logoneplusx", iface: "LogOnePlusX"],
[name: "maxconstant", iface: "MaxConstant"],
[name: "minconstant", iface: "MinConstant"],
[name: "multiply", iface: "Multiply", aliases: ["mul"]],
[name: "multiply", iface: "Multiply", aliases: ["mul", "product"]],
[name: "nearestint", iface: "NearestInt"],
[name: "negate", iface: "Negate"],
[name: "or", iface: "Or"],
Expand All @@ -111,7 +110,7 @@ ops = ```
[name: "sqr", iface: "Sqr"],
[name: "sqrt", iface: "Sqrt"],
[name: "step", iface: "Step"],
[name: "subtract", iface: "Subtract", aliases: ["sub"]],
[name: "subtract", iface: "Subtract", aliases: ["sub", "diff", "difference"]],
[name: "tan", iface: "Tan"],
[name: "tanh", iface: "Tanh"],
[name: "ulp", iface: "Ulp"],
Expand Down
57 changes: 57 additions & 0 deletions src/main/templates/net/imagej/ops/math/PrimitiveMath.list
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Generated unary and binary arithmetic ops with primitive types.
#
# Many of Java's binary numeric operators only work on ints and longs.
# And still more of them purport to work on bytes and shorts, but
# ultimately return int for the result. And most of Java's unary and
# binary Math functions only work on doubles. So rather than generate
# the entire cross-product of types-plus-operations, we do only those
# that are both valid and consistent regarding the return type.

[PrimitiveMath.java]
types = ```
[
[name: "Integer", primitive: "int", code: "i", priority: 0.4],
[name: "Long", primitive: "long", code: "l", priority: 0.3],
[name: "Float", primitive: "float", code: "f", priority: 0.2],
[name: "Double", primitive: "double", code: "d", priority: 0.1]
]
```
ops = ```
[
[name: "Add", operator: "+", unary: false, aliases: true, codes: "ilfd", verbs: "adds (+)"],
[name: "Subtract", operator: "-", unary: false, aliases: true, codes: "ilfd", verbs: "subtracts (-)"],
[name: "Multiply", operator: "*", unary: false, aliases: true, codes: "ilfd", verbs: "multiplies (*)"],
[name: "Divide", operator: "/", unary: false, aliases: true, codes: "ilfd", verbs: "divides (*)"],
[name: "Remainder", operator: "%", unary: false, aliases: true, codes: "ilfd", verbs: "computes the remainder (%) of"],
[name: "And", operator: "&", unary: false, aliases: false, codes: "il", verbs: "computes the bitwise AND (&) of"],
[name: "Or", operator: "|", unary: false, aliases: false, codes: "il", verbs: "computes the bitwise OR (|) of"],
[name: "Xor", operator: "^", unary: false, aliases: false, codes: "il", verbs: "computes the bitwise XOR (^) of"],
[name: "LeftShift", operator: "<<", unary: false, aliases: true, codes: "il", verbs: "computes the signed left shift (<<) of"],
[name: "RightShift", operator: ">>", unary: false, aliases: true, codes: "il", verbs: "computes the signed right shift (>>) of"],
[name: "UnsignedRightShift", operator: ">>>", unary: false, aliases: true, codes: "il", verbs: "computes the unsigned right shift (>>>) of"],
[name: "MaxConstant", function: "Math.max", unary: false, aliases: false, codes: "ilfd", verbs: "computes the maximum of"],
[name: "MinConstant", function: "Math.min", unary: false, aliases: false, codes: "ilfd", verbs: "computes the minimum of"],
[name: "PowerConstant", function: "Math.pow", unary: false, aliases: false, codes: "d", verbs: "exponentiate"],
[name: "Negate", operator: "-", unary: true, aliases: false, codes: "ilfd", verbs: "negates (-)"],
[name: "Abs", function: "Math.abs", unary: true, aliases: false, codes: "ilfd", verbs: "computes the absolute value of"],
[name: "Arccos", function: "Math.acos", unary: true, aliases: false, codes: "d", verbs: "computes the arccosine of"],
[name: "Arcsin", function: "Math.asin", unary: true, aliases: false, codes: "d", verbs: "computes the arcsine of"],
[name: "Arctan", function: "Math.atan", unary: true, aliases: false, codes: "d", verbs: "computes the arctangent of"],
[name: "Ceil", function: "Math.ceil", unary: true, aliases: false, codes: "d", verbs: "computes the ceiling of"],
[name: "Cos", function: "Math.cos", unary: true, aliases: false, codes: "d", verbs: "computes the cosine of"],
[name: "Cosh", function: "Math.cosh", unary: true, aliases: false, codes: "d", verbs: "computes the hyperbolic cosine of"],
[name: "CubeRoot", function: "Math.cbrt", unary: true, aliases: false, codes: "d", verbs: "computes the cubic root of"],
[name: "Exp", function: "Math.exp", unary: true, aliases: false, codes: "d", verbs: "computes the natural exponent of"],
[name: "Floor", function: "Math.floor", unary: true, aliases: false, codes: "d", verbs: "computes the floor of"],
[name: "Log", function: "Math.log", unary: true, aliases: false, codes: "d", verbs: "computes the natural logarithm of"],
[name: "Log10", function: "Math.log10", unary: true, aliases: false, codes: "d", verbs: "computes the base 10 logarithm of"],
[name: "LogOnePlusX", function: "Math.log1p", unary: true, aliases: false, codes: "d", verbs: "computes the natural logarithm of one plus"],
[name: "Round", function: "Math.round", unary: true, aliases: false, codes: "fd", verbs: "rounds"],
[name: "Signum", function: "Math.signum", unary: true, aliases: false, codes: "fd", verbs: "computes the signum of"],
[name: "Sin", function: "Math.sin", unary: true, aliases: false, codes: "d", verbs: "computes the sine of"],
[name: "Sinh", function: "Math.sinh", unary: true, aliases: false, codes: "d", verbs: "computes the hyperbolic sine of"],
[name: "Sqrt", function: "Math.sqrt", unary: true, aliases: false, codes: "d", verbs: "computes the square root of"],
[name: "Tan", function: "Math.tan", unary: true, aliases: false, codes: "d", verbs: "computes the tangent of"],
[name: "Tanh", function: "Math.tanh", unary: true, aliases: false, codes: "d", verbs: "computes the hyperbolic tangent of"]
]
```
87 changes: 87 additions & 0 deletions src/main/templates/net/imagej/ops/math/PrimitiveMath.vm
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* #%L
* ImageJ software for multidimensional image processing and analysis.
* %%
* Copyright (C) 2014 - 2015 Board of Regents of the University of
* Wisconsin-Madison, University of Konstanz and Brian Northan.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

package net.imagej.ops.math;

import net.imagej.ops.Op;
import net.imagej.ops.MathOps;

import org.scijava.ItemIO;
import org.scijava.plugin.Attr;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;

/**
* Generated arithmetic ops with primitive types.
*
* @author Curtis Rueden
*/
public final class PrimitiveMath {

private PrimitiveMath() {
// NB: Prevent instantiation of utility class.
}
#foreach ($type in $types)
#foreach ($op in $ops)
#if ($op.codes.contains($type.code))
#set ($iface = "MathOps.$op.name")

/** Op that $op.verbs#if ($op.unary) a $type.primitive value#else two $type.primitive values#end. */
@Plugin(type = Op.class, name = ${iface}.NAME#if ($type.priority), priority = $type.priority#end#if ($op.aliases), attrs = { @Attr(name = "aliases", value = ${iface}.ALIASES) }#end)
public static class $type.name$op.name implements $iface {
@Parameter(type = ItemIO.OUTPUT)
private $type.primitive result;

@Parameter
private $type.primitive a;
#if (!$op.unary)

@Parameter
private $type.primitive b;
#end

@Override
public void run() {
#if ($op.unary && $op.operator)
result = ${op.operator}a;
#elseif ($op.unary)
result = ${op.function}(a);
#elseif ($op.operator)
result = a $op.operator b;
#else
result = ${op.function}(a, b);
#end
}
}
#end
#end
#end

}

This file was deleted.

103 changes: 103 additions & 0 deletions src/test/templates/net/imagej/ops/math/TestPrimitiveMath.list
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Unit tests for the unary and binary arithmetic ops with primitive args.

[TestPrimitiveMath.java]
types = ```
[
D: [name: "Double", primitive: "double", delta: "0.0"],
F: [name: "Float", primitive: "float", delta: "0f"],
I: [name: "Integer", primitive: "int"],
L: [name: "Long", primitive: "long"]
]
```
tests = ```
[
[op: "Add", code: "D", result: "3.0", args: "1.0, 2.0"],
[op: "Add", code: "F", result: "8f", args: "3f, 5f"],
[op: "Add", code: "I", result: "-7", args: "11, -18"],
[op: "Add", code: "L", result: "1111111111111111110L", args: "123456789987654321L, 987654321123456789L"],

[op: "Subtract", code: "D", result: "-1.0", args: "1.0, 2.0"],
[op: "Subtract", code: "F", result: "2f", args: "5f, 3f"],
[op: "Subtract", code: "I", result: "31", args: "12, -19"],
[op: "Subtract", code: "L", result: "864197531135802468L", args: "987654321123456789L, 123456789987654321L"],

[op: "Multiply", code: "D", result: "8.4", args: "2.4, 3.5"],
[op: "Multiply", code: "F", result: "15.84f", args: "4.4f, 3.6f"],
[op: "Multiply", code: "I", result: "-6", args: "2, -3"],
[op: "Multiply", code: "L", result: "8747884673140948462L", args: "1234567887654321L, 8765432112345678L"],

[op: "Divide", code: "D", result: "2.0e11", args: "2.2e34, 1.1e23"],
[op: "Divide", code: "F", result: "1.375f", args: "4.4f, 3.2f"],
[op: "Divide", code: "I", result: "3", args: "11, 3"],
[op: "Divide", code: "L", result: "80000L", args: "987654321123456789L, 12345677654321L"],

[op: "Remainder", code: "D", result: "5.914556415807146e120", args: "2.2e234, 1.1e123"],
[op: "Remainder", code: "F", result: "2.5f", args: "9.3f, 3.4f"],
[op: "Remainder", code: "I", result: "2", args: "11, 3"],
[op: "Remainder", code: "L", result: "108777776789L", args: "987654321123456789L, 12345677654321L"],

[op: "And", code: "I", result: "0xcaacbaae", args: "0xcafebabe, 0xdeadbeef"],
[op: "And", code: "L", result: "0x9eadbabecaac8aeeL", args: "0xdeadbabecafebeefL, 0xbeefbabedeadcafeL"],

[op: "Or", code: "I", result: "0xdeffbeff", args: "0xcafebabe, 0xdeadbeef"],
[op: "Or", code: "L", result: "0xfeefbabedefffeffL", args: "0xdeadbabecafebeefL, 0xbeefbabedeadcafeL"],

[op: "Xor", code: "I", result: "0x14530451", args: "0xcafebabe, 0xdeadbeef"],
[op: "Xor", code: "L", result: "0x6042000014537411L", args: "0xdeadbabecafebeefL, 0xbeefbabedeadcafeL"],
[op: "LeftShift", code: "I", result: "0x5d5f0000", args: "0xcafebabe, 15"],
[op: "LeftShift", code: "L", result: "0x6f56e57f00000000L", args: "0xbeefbabedeadcafeL, 31"],

[op: "RightShift", code: "I", result: "0xffff95fd", args: "0xcafebabe, 15"],
[op: "RightShift", code: "L", result: "0xffffffff7ddf757dL", args: "0xbeefbabedeadcafeL, 31"],

[op: "UnsignedRightShift", code: "I", result: "0x195fd", args: "0xcafebabe, 15"],
[op: "UnsignedRightShift", code: "L", result: "0x17ddf757dL", args: "0xbeefbabedeadcafeL, 31"],

[op: "MaxConstant", code: "D", result: "1.6", args: "1.6, 1.55"],
[op: "MaxConstant", code: "F", result: "-3.2f", args: "-4.8f, -3.2f"],
[op: "MaxConstant", code: "I", result: "8", args: "3, 8"],
[op: "MaxConstant", code: "L", result: "987654321123456789L", args: "123456789987654321L, 987654321123456789L"],

[op: "MinConstant", code: "D", result: "1.55", args: "1.6, 1.55"],
[op: "MinConstant", code: "F", result: "-4.8f", args: "-4.8f, -3.2f"],
[op: "MinConstant", code: "I", result: "11", args: "11, 571"],
[op: "MinConstant", code: "L", result: "123456789987654321L", args: "123456789987654321L, 987654321123456789L"],

[op: "PowerConstant", code: "D", result: "8.0", args: "2.0, 3.0"],

[op: "Negate", code: "D", result: "-3.0", args: "3.0"],
[op: "Negate", code: "F", result: "3f", args: "-3f"],
[op: "Negate", code: "I", result: "-3", args: "3"],
[op: "Negate", code: "L", result: "123456789987654321L", args: "-123456789987654321L"],

[op: "Abs", code: "D", result: "2.0", args: "-2.0"],
[op: "Abs", code: "F", result: "2f", args: "-2f"],
[op: "Abs", code: "I", result: "2", args: "-2"],
[op: "Abs", code: "L", result: "123456789987654321L", args: "-123456789987654321L"],

[op: "Arccos", code: "D", result: "0.6599873293874984", args: "0.79"],
[op: "Arcsin", code: "D", result: "0.9108089974073983", args: "0.79"],
[op: "Arctan", code: "D", result: "0.6686135679278209", args: "0.79"],
[op: "Ceil", code: "D", result: "1.0", args: "0.79"],
[op: "Cos", code: "D", result: "0.7038453156522361", args: "0.79"],
[op: "Cosh", code: "D", result: "1.3286206107691463", args: "0.79"],
[op: "CubeRoot", code: "D", result: "0.9244335465376482", args: "0.79"],
[op: "Exp", code: "D", result: "2.203396426255937", args: "0.79"],
[op: "Floor", code: "D", result: "0.0", args: "0.79"],
[op: "Log", code: "D", result: "-0.23572233352106983", args: "0.79"],
[op: "Log10", code: "D", result: "-0.10237290870955855", args: "0.79"],
[op: "LogOnePlusX", code: "D", result: "0.5822156198526637", args: "0.79"],

[op: "Round", code: "D", result: "1.0", args: "0.79"],
[op: "Round", code: "F", result: "2f", args: "1.57f"],

[op: "Signum", code: "D", result: "1.0", args: "0.79"],
[op: "Signum", code: "F", result: "-1f", args: "-4.56f"],

[op: "Sin", code: "D", result: "0.7103532724176078", args: "0.79"],
[op: "Sinh", code: "D", result: "0.8747758154867904", args: "0.79"],
[op: "Sqrt", code: "D", result: "0.8888194417315589", args: "0.79"],
[op: "Tan", code: "D", result: "1.0092462883827549", args: "0.79"],
[op: "Tanh", code: "D", result: "0.6584090359552511", args: "0.79"]
]
```
Loading

0 comments on commit af05aff

Please sign in to comment.