sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1654372 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ sis-utility/src/main/java/org/apache/sis/math/ sis-utility/src/test/java/org/apache/sis/math/
Date Fri, 23 Jan 2015 20:34:08 GMT
Author: desruisseaux
Date: Fri Jan 23 20:34:08 2015
New Revision: 1654372

URL: http://svn.apache.org/r1654372
Log:
Plane fields are no longer public - use accessor instead.
The intend is to keep room for improvement if a future version want to store values with
double-double arithmetic (for now only the 'fit' method needs such arithmetic), or if we
want to support planes with vertical orientation (sx ou sy term tending toward infinity).
Also rewrite the equations in the javadoc for using the more common order where the constant
is last.

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java?rev=1654372&r1=1654371&r2=1654372&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
[UTF-8] Fri Jan 23 20:34:08 2015
@@ -153,9 +153,9 @@ public class LinearTransformBuilder {
         correlation = new double[targetDim];
         for (int j=0; j<targets.length; j++) {
             correlation[j] = plan.fit(sources[0], sources[1], targets[j]);
-            matrix.setElement(j, 0, plan.cx);
-            matrix.setElement(j, 1, plan.cy);
-            matrix.setElement(j, 2, plan.c);
+            matrix.setElement(j, 0, plan.slopeX());
+            matrix.setElement(j, 1, plan.slopeY());
+            matrix.setElement(j, 2, plan.z0());
         }
         matrix.setElement(targetDim, sourceDim, 1);
         return MathTransforms.linear(matrix);

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java?rev=1654372&r1=1654371&r2=1654372&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8]
(original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Plane.java [UTF-8]
Fri Jan 23 20:34:08 2015
@@ -23,14 +23,19 @@ import org.apache.sis.internal.util.Doub
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.resources.Errors;
 
+import static java.lang.Math.abs;
+import static java.lang.Math.sqrt;
+import static java.lang.Math.ulp;
+
 
 /**
  * Equation of a plane in a three-dimensional space (<var>x</var>,<var>y</var>,<var>z</var>).
- * The plane equation is expressed by {@link #c}, {@link #cx} and {@link #cy} coefficients
as below:
+ * The plane equation is expressed by {@linkplain #slopeX() sx}, {@linkplain #slopeY() sy}
and
+ * {@linkplain #z0() z₀} coefficients as below:
  *
  * <blockquote>
  *   <var>{@linkplain #z(double, double) z}</var>(<var>x</var>,<var>y</var>)
=
- *   <var>c</var> + <var>cx</var>⋅<var>x</var> + <var>cy</var>⋅<var>y</var>
+ *   <var>sx</var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var>
+ <var>z</var>₀
  * </blockquote>
  *
  * Those coefficients can be set directly, or computed by a linear regression of this plane
@@ -49,7 +54,7 @@ public class Plane implements Cloneable,
     private static final long serialVersionUID = 2956201711131316723L;
 
     /**
-     * Threshold value relative to 1 ULP of other terms in the  z = c + cx⋅x + cy⋅y 
equation.
+     * Threshold value relative to 1 ULP of other terms in the  z = sx⋅x + sy⋅y + z₀
 equation.
      * A value of 1 would be theoretically sufficient since adding a value smaller to 1 ULP
to
      * a {@code double} has no effect. Nevertheless we use a smaller value as a safety because:
      *
@@ -69,69 +74,132 @@ public class Plane implements Cloneable,
     private static final double ZERO_THRESHOLD = 1E-14;
 
     /**
-     * The <var>c</var> coefficient for this plane. This coefficient appears
in the plane equation
-     * <var><b><u>c</u></b></var> + <var>cx</var>⋅<var>x</var>
+ <var>cy</var>⋅<var>y</var>.
+     * The slope along the <var>x</var> values. This coefficient appears in the
plane equation
+     * <var><b><u>sx</u></b></var>⋅<var>x</var>
+ <var>sy</var>⋅<var>y</var> + <var>z</var>₀.
      */
-    public double c;
+    private double sx;
 
     /**
-     * The <var>cx</var> coefficient for this plane. This coefficient appears
in the plane equation
-     * <var>c</var> + <var><b><u>cx</u></b></var>⋅<var>x</var>
+ <var>cy</var>⋅<var>y</var>.
+     * The slope along the <var>y</var> values. This coefficient appears in the
plane equation
+     * <var>sx</var>⋅<var>x</var> + <var><b><u>sy</u></b></var>⋅<var>y</var>
+ <var>z</var>₀.
      */
-    public double cx;
+    private double sy;
 
     /**
-     * The <var>cy</var> coefficient for this plane. This coefficient appears
in the place equation
-     * <var>c</var> + <var>cx</var>⋅<var>x</var> +
<var><b><u>cy</u></b></var>⋅<var>y</var>.
+     * The <var>z</var> value at (<var>x</var>,<var>y</var>)
= (0,0). This coefficient appears in the plane equation
+     * <var>sx</var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var>
+ <b><u><var>z</var>₀</u></b>.
      */
-    public double cy;
+    private double z0;
 
     /**
-     * Constructs a new plane with all coefficients set to 0.
+     * Constructs a new plane with all coefficients initialized to {@link Double#NaN}.
      */
     public Plane() {
+        sx = sy = z0 = Double.NaN;
+    }
+
+    /**
+     * Constructs a new plane initialized to the given coefficients.
+     *
+     * @param sx The slope along the <var>x</var> values.
+     * @param sy The slope along the <var>y</var> values.
+     * @param z0 The <var>z</var> value at (<var>x</var>,<var>y</var>)
= (0,0).
+     *
+     * @see #setEquation(double, double, double)
+     */
+    public Plane(final double sx, final double sy, final double z0) {
+        this.sx = sx;
+        this.sy = sy;
+        this.z0 = z0;
+    }
+
+    /**
+     * Returns the slope along the <var>x</var> values. This coefficient appears
in the plane equation
+     * <var><b><u>sx</u></b></var>⋅<var>x</var>
+ <var>sy</var>⋅<var>y</var> + <var>z</var>₀.
+     *
+     * @return The <var>sx</var> term.
+     */
+    public final double slopeX() {
+        return sx;
+    }
+
+    /**
+     * Returns the slope along the <var>y</var> values. This coefficient appears
in the plane equation
+     * <var>sx</var>⋅<var>x</var> + <var><b><u>sy</u></b></var>⋅<var>y</var>
+ <var>z</var>₀.
+     *
+     * @return The <var>sy</var> term.
+     */
+    public final double slopeY() {
+        return sy;
+    }
+
+    /**
+     * Returns the <var>z</var> value at (<var>x</var>,<var>y</var>)
= (0,0). This coefficient appears in the
+     * plane equation <var>sx</var>⋅<var>x</var> + <var>sy</var>⋅<var>y</var>
+ <b><u><var>z</var>₀</u></b>.
+     *
+     * @return The <var>z</var>₀ term.
+     *
+     * @see #z(double, double)
+     */
+    public final double z0() {
+        return z0;
     }
 
     /**
      * Computes the <var>x</var> value for the specified (<var>y</var>,<var>z</var>)
point.
      * The <var>x</var> value is computed using the following equation:
      *
-     * <blockquote>x(y,z) = (z - ({@linkplain #c} + {@linkplain #cy}⋅y)) / {@linkplain
#cx}</blockquote>
+     * <blockquote>x(y,z) = (z - ({@linkplain #z0() z0} + {@linkplain #slopeY() sy}⋅y))
/ {@linkplain #slopeX() sx}</blockquote>
      *
-     * @param y The <var>x</var> value.
-     * @param z The <var>y</var> value.
+     * @param y The <var>y</var> value where to compute <var>x</var>.
+     * @param z The <var>z</var> value where to compute <var>x</var>.
      * @return  The <var>x</var> value.
      */
     public final double x(final double y, final double z) {
-        return (z - (c + cy*y)) / cx;
+        return (z - (z0 + sy*y)) / sx;
     }
 
     /**
      * Computes the <var>y</var> value for the specified (<var>x</var>,<var>z</var>)
point.
      * The <var>y</var> value is computed using the following equation:
      *
-     * <blockquote>y(x,z) = (z - ({@linkplain #c} + {@linkplain #cx}⋅x)) / {@linkplain
#cy}</blockquote>
+     * <blockquote>y(x,z) = (z - ({@linkplain #z0() z0} + {@linkplain #slopeX() sx}⋅x))
/ {@linkplain #slopeY() sy}</blockquote>
      *
-     * @param x The <var>x</var> value.
-     * @param z The <var>y</var> value.
+     * @param x The <var>x</var> value where to compute <var>y</var>.
+     * @param z The <var>z</var> value where to compute <var>y</var>.
      * @return  The <var>y</var> value.
      */
     public final double y(final double x, final double z) {
-        return (z - (c + cx*x)) / cy;
+        return (z - (z0 + sx*x)) / sy;
     }
 
     /**
      * Computes the <var>z</var> value for the specified (<var>x</var>,<var>y</var>)
point.
      * The <var>z</var> value is computed using the following equation:
      *
-     * <blockquote>z(x,y) = {@linkplain #c} + {@linkplain #cx}⋅x + {@linkplain #cy}⋅y</blockquote>
+     * <blockquote>z(x,y) = {@linkplain #z0() z0} + {@linkplain #slopeX() sx}⋅x +
{@linkplain #slopeY() sy}⋅y</blockquote>
      *
-     * @param x The <var>x</var> value.
-     * @param y The <var>y</var> value.
+     * @param x The <var>x</var> value where to compute <var>z</var>.
+     * @param y The <var>y</var> value where to compute <var>z</var>.
      * @return  The <var>z</var> value.
+     *
+     * @see #z0()
      */
     public final double z(final double x, final double y) {
-        return c + cx*x + cy*y;
+        return z0 + sx*x + sy*y;
+    }
+
+    /**
+     * Sets the equation of this plane to the given coefficients.
+     *
+     * @param sx The slope along the <var>x</var> values.
+     * @param sy The slope along the <var>y</var> values.
+     * @param z0 The <var>z</var> value at (<var>x</var>,<var>y</var>)
= (0,0).
+     */
+    public void setEquation(final double sx, final double sy, final double z0) {
+        this.sx = sx;
+        this.sy = sy;
+        this.z0 = z0;
     }
 
     /**
@@ -179,7 +247,7 @@ public class Plane implements Cloneable,
      * The second pass can also opportunistically checks if some small coefficients can be
replaced by zero.
      */
     private double fit(final Iterable<? extends DirectPosition> points,
-            boolean detectZeroC, boolean detectZeroCx, boolean detectZeroCy)
+            boolean detectZeroSx, boolean detectZeroSy, boolean detectZeroZ0)
     {
         int i = 0, n = 0;
         final DoubleDouble sum_x  = new DoubleDouble();
@@ -221,8 +289,8 @@ public class Plane implements Cloneable,
             n++;
         }
         /*
-         *    ( sum_zx - sum_z*sum_x )  =  cx*(sum_xx - sum_x*sum_x) + cy*(sum_xy - sum_x*sum_y)
-         *    ( sum_zy - sum_z*sum_y )  =  cx*(sum_xy - sum_x*sum_y) + cy*(sum_yy - sum_y*sum_y)
+         *    ( sum_zx - sum_z*sum_x )  =  sx*(sum_xx - sum_x*sum_x) + sy*(sum_xy - sum_x*sum_y)
+         *    ( sum_zy - sum_z*sum_y )  =  sx*(sum_xy - sum_x*sum_y) + sy*(sum_yy - sum_y*sum_y)
          */
         zx.setFrom(sum_x); zx.divide(-n, 0); zx.multiply(sum_z); zx.add(sum_zx);    // zx
= sum_zx - sum_z*sum_x/n
         zy.setFrom(sum_y); zy.divide(-n, 0); zy.multiply(sum_z); zy.add(sum_zy);    // zy
= sum_zy - sum_z*sum_y/n
@@ -236,26 +304,26 @@ public class Plane implements Cloneable,
         final DoubleDouble den = new DoubleDouble(xy); den.multiply(xy);
         den.subtract(tmp);
         /*
-         * cx = (zy*xy - zx*yy) / den
-         * cy = (zx*xy - zy*xx) / den
-         * c  = (sum_z - (cx*sum_x + cy*sum_y)) / n
+         * sx = (zy*xy - zx*yy) / den
+         * sy = (zx*xy - zy*xx) / den
+         * z₀ = (sum_z - (sx*sum_x + sy*sum_y)) / n
          */
-        final DoubleDouble cx = new DoubleDouble(zy); cx.multiply(xy); tmp.setFrom(zx); tmp.multiply(yy);
cx.subtract(tmp); cx.divide(den);
-        final DoubleDouble cy = new DoubleDouble(zx); cy.multiply(xy); tmp.setFrom(zy); tmp.multiply(xx);
cy.subtract(tmp); cy.divide(den);
-        final DoubleDouble c  = new DoubleDouble(cy);
-        c.multiply(sum_y);
-        tmp.setFrom(cx);
+        final DoubleDouble sx = new DoubleDouble(zy); sx.multiply(xy); tmp.setFrom(zx); tmp.multiply(yy);
sx.subtract(tmp); sx.divide(den);
+        final DoubleDouble sy = new DoubleDouble(zx); sy.multiply(xy); tmp.setFrom(zy); tmp.multiply(xx);
sy.subtract(tmp); sy.divide(den);
+        final DoubleDouble z0 = new DoubleDouble(sy);
+        z0.multiply(sum_y);
+        tmp.setFrom(sx);
         tmp.multiply(sum_x);
-        tmp.add(c);
-        c.setFrom(sum_z);
-        c.subtract(tmp);
-        c.divide(n, 0);
+        tmp.add(z0);
+        z0.setFrom(sum_z);
+        z0.subtract(tmp);
+        z0.divide(n, 0);
         /*
          * Done - store the result.
          */
-        this.c  = c .value;
-        this.cx = cx.value;
-        this.cy = cy.value;
+        this.sx = sx.value;
+        this.sy = sy.value;
+        this.z0 = z0.value;
         /*
          * At this point, the model is computed. Now computes an estimation of the Pearson
          * correlation coefficient. Note that both the z array and the z computed from the
@@ -267,11 +335,11 @@ public class Plane implements Cloneable,
         final double mean_x = sum_x.value / n;
         final double mean_y = sum_y.value / n;
         final double mean_z = sum_z.value / n;
-        final double offset = Math.abs((this.cx * mean_x + this.cy * mean_y) + this.c); //
Offsetted c - see comment before usage.
+        final double offset = abs((this.sx * mean_x + this.sy * mean_y) + this.z0); // Offsetted
z₀ - see comment before usage.
         double sum_ds2 = 0, sum_dz2 = 0, sum_dsz = 0;
         for (final DirectPosition p : points) {
-            final double x = (p.getOrdinate(0) - mean_x) * cx.value;
-            final double y = (p.getOrdinate(1) - mean_y) * cy.value;
+            final double x = (p.getOrdinate(0) - mean_x) * sx.value;
+            final double y = (p.getOrdinate(1) - mean_y) * sy.value;
             final double z = (p.getOrdinate(2) - mean_z);
             final double s = x + y;
             if (!Double.isNaN(s) && !Double.isNaN(z)) {
@@ -281,25 +349,25 @@ public class Plane implements Cloneable,
             }
             /*
              * Algorithm for detecting if a coefficient should be zero:
-             * If for every points given by the user, adding (cx⋅x) in (c + cx⋅x + cy⋅y)
does not make any difference
-             * because (cx⋅x) is smaller than 1 ULP of (c + cy⋅y), then it is not worth
adding it and  cx  can be set
-             * to zero. The same rational applies to (cy⋅y) and c.
+             * If for every points given by the user, adding (sx⋅x) in (sx⋅x + sy⋅y
+ z₀) does not make any difference
+             * because (sx⋅x) is smaller than 1 ULP of (sy⋅y + z₀), then it is not
worth adding it and  sx  can be set
+             * to zero. The same rational applies to (sy⋅y) and z₀.
              *
-             * Since we work with differences from the means, the  z = c + cx⋅x + cy⋅y
 equation can be rewritten as:
+             * Since we work with differences from the means, the  z = sx⋅x + sy⋅y +
z₀  equation can be rewritten as:
              *
-             *     Δz = cx⋅Δx + cy⋅Δy + (cx⋅mx + cy⋅my + c - mz)    where the
term between (…) is close to zero.
+             *     Δz = sx⋅Δx + sy⋅Δy + (sx⋅mx + sy⋅my + z₀ - mz)    where the
term between (…) is close to zero.
              *
-             * The check for (cx⋅Δx) and (cy⋅Δy) below ignore the (…) term since
it is close to zero.
-             * The check for  c  is derived from an equation without the  -mz  term.
+             * The check for (sx⋅Δx) and (sy⋅Δy) below ignore the (…) term since
it is close to zero.
+             * The check for  z₀  is derived from an equation without the  -mz  term.
              */
-            if (detectZeroC  && offset      >= Math.ulp(s * ZERO_THRESHOLD)) detectZeroC
 = false;
-            if (detectZeroCx && Math.abs(x) >= Math.ulp(y * ZERO_THRESHOLD)) detectZeroCx
= false;
-            if (detectZeroCy && Math.abs(y) >= Math.ulp(x * ZERO_THRESHOLD)) detectZeroCy
= false;
+            if (detectZeroSx && abs(x) >= ulp(y * ZERO_THRESHOLD)) detectZeroSx
= false;
+            if (detectZeroSy && abs(y) >= ulp(x * ZERO_THRESHOLD)) detectZeroSy
= false;
+            if (detectZeroZ0 && offset >= ulp(s * ZERO_THRESHOLD)) detectZeroZ0
= false;
         }
-        if (detectZeroC)  this.c  = 0;
-        if (detectZeroCx) this.cx = 0;
-        if (detectZeroCy) this.cy = 0;
-        return sum_dsz / Math.sqrt(sum_ds2 * sum_dz2);
+        if (detectZeroSx) this.sx = 0;
+        if (detectZeroSy) this.sy = 0;
+        if (detectZeroZ0) this.z0 = 0;
+        return sum_dsz / sqrt(sum_ds2 * sum_dz2);
     }
 
     /**
@@ -326,9 +394,9 @@ public class Plane implements Cloneable,
     public boolean equals(final Object object) {
         if (object != null && getClass() == object.getClass()) {
             final Plane that = (Plane) object;
-            return Numerics.equals(this.c,  that.c ) &&
-                   Numerics.equals(this.cx, that.cx) &&
-                   Numerics.equals(this.cy, that.cy);
+            return Numerics.equals(this.z0,  that.z0 ) &&
+                   Numerics.equals(this.sx, that.sx) &&
+                   Numerics.equals(this.sy, that.sy);
         } else {
             return false;
         }
@@ -340,9 +408,9 @@ public class Plane implements Cloneable,
     @Override
     public int hashCode() {
         return Numerics.hashCode(serialVersionUID
-                     ^ (Double.doubleToLongBits(c )
-                + 31 * (Double.doubleToLongBits(cx)
-                + 31 * (Double.doubleToLongBits(cy)))));
+                     ^ (Double.doubleToLongBits(z0 )
+                + 31 * (Double.doubleToLongBits(sx)
+                + 31 * (Double.doubleToLongBits(sy)))));
     }
 
     /**
@@ -350,28 +418,26 @@ public class Plane implements Cloneable,
      * The string will contains the plane's equation, as below:
      *
      * <blockquote>
-     *     <var>z</var>(<var>x</var>,<var>y</var>) =
{@link #c} +
-     *     {@link #cx}⋅<var>x</var> + {@link #cy}⋅<var>y</var>
+     *     <var>z</var>(<var>x</var>,<var>y</var>) =
{@linkplain #slopeX() sx}⋅<var>x</var>
+     *     + {@linkplain #slopeY() sy}⋅<var>y</var> + {@linkplain #z0() z0}
      * </blockquote>
      */
     @Override
     public String toString() {
         final StringBuilder buffer = new StringBuilder("z(x,y) = ");
-        if (c == 0 && cx == 0 && cy == 0) {
+        String separator = "";
+        if (sx != 0) {
+            buffer.append(sx).append("⋅x");
+            separator = " + ";
+        }
+        if (sy != 0) {
+            buffer.append(separator).append(sy).append("⋅y");
+            separator = " + ";
+        }
+        if (z0 != 0) {
+            buffer.append(separator).append(z0);
+        } else if (separator.isEmpty()) {
             buffer.append('0');
-        } else {
-            if (c != 0) {
-                buffer.append(c).append(" + ");
-            }
-            if (cx != 0) {
-                buffer.append(cx).append("⋅x");
-                if (cy != 0) {
-                    buffer.append(" + ");
-                }
-            }
-            if (cy != 0) {
-                buffer.append(cy).append("⋅y");
-            }
         }
         return buffer.toString();
     }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java?rev=1654372&r1=1654371&r2=1654372&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java [UTF-8]
(original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/math/PlaneTest.java [UTF-8]
Fri Jan 23 20:34:08 2015
@@ -97,18 +97,15 @@ public final strictfp class PlaneTest ex
         final double x[] = random(rd, 4000, 100, -25);
         final double y[] = random(rd, 4000,  80, -20);
         final double z[] = random(rd, 4000, 150, -40);
-        final Plane plan = new Plane();
-        plan.cx =  2;
-        plan.cy =  3;
-        plan.c  = -4;
+        final Plane plan = new Plane(2, 3, -4);
         for (int i=0; i<z.length; i++) {
             // Compute points with random displacements above or below a known plane.
             z[i] = plan.z(x[i], y[i]) + (10 * rd.nextDouble() - 5);
         }
         final Plane fitted = assertFitEquals(6, x, y, z);
-        assertEquals("cx", plan.cx, fitted.cx, 0.01);
-        assertEquals("cy", plan.cy, fitted.cy, 0.01);
-        assertEquals("c",  plan.c,  fitted.c,  1);
+        assertEquals("sx", plan.slopeX(), fitted.slopeX(), 0.01);
+        assertEquals("sy", plan.slopeY(), fitted.slopeY(), 0.01);
+        assertEquals("z₀", plan.z0(),     fitted.z0(),     1);
     }
 
     /**
@@ -116,10 +113,7 @@ public final strictfp class PlaneTest ex
      */
     @Test
     public void testSerialization() {
-        final Plane local = new Plane();
-        local.c  =  3.7;
-        local.cx =  9.3;
-        local.cy = -1.8;
+        final Plane local = new Plane(3.7, 9.3, -1.8);
         assertNotSame(local, assertSerializedEquals(local));
     }
 }



Mime
View raw message