You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2018/07/06 22:41:55 UTC

[commons-geometry] 08/08: GEOMETRY-3: simplifying parse and format functionality; renaming Coordinates.Factory?D interfaces to be more generic RealFunction interfaces

This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch GEOMETRY-3__TBR
in repository https://gitbox.apache.org/repos/asf/commons-geometry.git

commit 2331c8a18296ae25e1b4b85b98bc7b7dbb718d05
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Sat Jun 30 00:53:10 2018 -0400

    GEOMETRY-3: simplifying parse and format functionality; renaming Coordinates.Factory?D interfaces to be more generic RealFunction interfaces
---
 .../core/util/AbstractCoordinateParser.java        | 257 ------------
 .../commons/geometry/core/util/Coordinates.java    |  68 ----
 .../commons/geometry/core/util/RealFunction.java   |  31 ++
 .../commons/geometry/core/util/RealFunction2N.java |  32 ++
 .../commons/geometry/core/util/RealFunction3N.java |  33 ++
 .../geometry/core/util/SimpleCoordinateFormat.java | 202 ----------
 .../core/util/internal/SimpleTupleFormat.java      | 430 +++++++++++++++++++++
 .../geometry/core/util/internal/package-info.java  |  24 ++
 .../SimpleTupleFormatTest.java}                    |  88 ++---
 .../geometry/euclidean/oned/Cartesian1D.java       |   7 +
 .../commons/geometry/euclidean/oned/Point1D.java   |  20 +-
 .../commons/geometry/euclidean/oned/Vector1D.java  |  20 +-
 .../geometry/euclidean/threed/Cartesian3D.java     |   7 +
 .../commons/geometry/euclidean/threed/Point3D.java |  20 +-
 .../geometry/euclidean/threed/Vector3D.java        |  20 +-
 .../geometry/euclidean/twod/Cartesian2D.java       |   7 +
 .../commons/geometry/euclidean/twod/Point2D.java   |  20 +-
 .../commons/geometry/euclidean/twod/Vector2D.java  |  20 +-
 .../geometry/euclidean/oned/Cartesian1DTest.java   |  16 +
 .../geometry/euclidean/oned/Point1DTest.java       |   8 +-
 .../geometry/euclidean/oned/Vector1DTest.java      |  20 +-
 .../geometry/euclidean/threed/Cartesian3DTest.java |  16 +
 .../geometry/euclidean/threed/Point3DTest.java     |   8 +-
 .../geometry/euclidean/threed/Vector3DTest.java    |  18 +-
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  16 +
 .../geometry/euclidean/twod/Point2DTest.java       |   8 +-
 .../geometry/euclidean/twod/Vector2DTest.java      |  18 +-
 .../commons/geometry/spherical/oned/S1Point.java   |  16 +-
 .../commons/geometry/spherical/twod/S2Point.java   |  16 +-
 .../geometry/spherical/oned/S1PointTest.java       |  10 +-
 .../geometry/spherical/twod/S2PointTest.java       |   8 +-
 31 files changed, 765 insertions(+), 719 deletions(-)

diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/AbstractCoordinateParser.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/AbstractCoordinateParser.java
deleted file mode 100644
index ceb8527..0000000
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/AbstractCoordinateParser.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * 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.
- */
-package org.apache.commons.geometry.core.util;
-
-import java.text.ParsePosition;
-
-/** Abstract class providing basic parsing functionality for reading coordinate tuples
- * from strings.
- */
-public abstract class AbstractCoordinateParser {
-
-    /** String separating coordinate values */
-    private final String separator;
-
-    /** String used to signal the start of a coordinate tuple; may be null */
-    private final String prefix;
-
-    /** String used to signal the end of a coordinate tuple; may be null */
-    private final String suffix;
-
-    /** Simple constructor
-     * @param separator String used to separate coordinate values; must not be null.
-     * @param prefix String used to signal the start of a coordinate tuple; if null, no
-     *      string is expected at the start of the tuple
-     * @param suffix String used to signal the end of a coordinate tuple; if null, no
-     *      string is expected at the end of the tuple
-     */
-    protected AbstractCoordinateParser(String separator, String prefix, String suffix) {
-        this.separator = separator;
-        this.prefix = prefix;
-        this.suffix = suffix;
-    }
-
-    /** Returns the string used to separate coordinate values.
-     * @return the coordinate value separator string
-     */
-    public String getSeparator() {
-        return separator;
-    }
-
-    /** Returns the string used to signal the start of a coordinate tuple. This value may be null.
-     * @return the string used to begin each coordinate tuple or null
-     */
-    public String getPrefix() {
-        return prefix;
-    }
-
-    /** Returns the string used to signal the end of a coordinate tuple. This value may be null.
-     * @return the string used to end each coordinate tuple or null
-     */
-    public String getSuffix() {
-        return suffix;
-    }
-
-    /** Reads the configured prefix from the current position in the given string, ignoring any preceding
-     * whitespace, and advances the parsing position past the prefix sequence. An exception is thrown if the
-     * prefix is not found. Does nothing if the prefix is null.
-     * @param str the string being parsed
-     * @param pos the current parsing position
-     * @throws IllegalArgumentException if the configured prefix is not null and is not found at the current
-     *      parsing position, ignoring preceding whitespace
-     */
-    protected void readPrefix(String str, ParsePosition pos) throws IllegalArgumentException {
-        if (prefix != null) {
-            consumeWhitespace(str, pos);
-            readSequence(str, prefix, pos);
-        }
-    }
-
-    /** Reads and returns a coordinate value from the current position in the given string. An exception is thrown if a
-     * valid number is not found. The parsing position is advanced past the parsed number and any trailing separator.
-     * @param str the string being parsed
-     * @param pos the current parsing position
-     * @return the coordinate value
-     * @throws IllegalArgumentException if the configured prefix is not null and is not found at the current
-     *      parsing position, ignoring preceding whitespace
-     */
-    protected double readCoordinateValue(String str, ParsePosition pos) throws IllegalArgumentException {
-        final int startIdx = pos.getIndex();
-
-        int endIdx = str.indexOf(separator, startIdx);
-        if (endIdx < 0) {
-            if (suffix != null) {
-                endIdx = str.indexOf(suffix, startIdx);
-            }
-
-            if (endIdx < 0) {
-                endIdx = str.length();
-            }
-        }
-
-        String substr = str.substring(startIdx, endIdx);
-        try {
-            double value = Double.parseDouble(substr);
-
-            // advance the position and move past any terminating separator
-            pos.setIndex(endIdx);
-            matchSequence(str, separator, pos);
-
-            return value;
-        }
-        catch (NumberFormatException exc) {
-            fail(String.format("unable to parse number from string \"%s\"", substr), str, pos, exc);
-            return 0.0; // for the compiler
-        }
-    }
-
-    /** Reads the configured suffix from the current position in the given string, ignoring any preceding
-     * whitespace, and advances the parsing position past the suffix sequence. An exception is thrown if the
-     * suffix is not found. Does nothing if the suffix is null.
-     * @param str the string being parsed
-     * @param pos the current parsing position
-     * @throws IllegalArgumentException if the configured suffix is not null and is not found at the current
-     *      parsing position, ignoring preceding whitespace
-     */
-    protected void readSuffix(String str, ParsePosition pos) throws IllegalArgumentException {
-        if (suffix != null) {
-            consumeWhitespace(str, pos);
-            readSequence(str, suffix, pos);
-        }
-    }
-
-    /** Ends a parse operation by ensuring that all non-whitespace characters in the string have been parsed. An
-     * exception is thrown if extra content is found.
-     * @param str the string being parsed
-     * @param pos the current parsing position
-     * @throws IllegalArgumentException if extra non-whitespace content is found past the current parsing position
-     */
-    protected void endParse(String str, ParsePosition pos) throws IllegalArgumentException {
-        consumeWhitespace(str, pos);
-        if (pos.getIndex() != str.length()) {
-            fail("unexpected content", str, pos);
-        }
-    }
-
-    /** Advances {@code pos} past any whitespace characters in {@code str},
-     * starting at the current parse position index.
-     * @param str the input string
-     * @param pos the current parse position
-     */
-    protected void consumeWhitespace(String str, ParsePosition pos) {
-        int idx = pos.getIndex();
-        final int len = str.length();
-
-        for (; idx<len; ++idx) {
-            if (!Character.isWhitespace(str.codePointAt(idx))) {
-                break;
-            }
-        }
-
-        pos.setIndex(idx);
-    }
-
-    /** Returns a boolean indicating whether or not the input string {@code str}
-     * contains the string {@code seq} at the given parse index. If the match succeeds,
-     * the index of {@code pos} is moved to the first character after the match. If
-     * the match does not succeed, the parse position is left unchanged.
-     * @param str the string to match against
-     * @param seq the sequence to look for in {@code str}
-     * @param pos the parse position indicating the index in {@code str}
-     *      to attempt the match
-     * @return true if {@code str} contains exactly the same characters as {@code seq}
-     *      at {@code pos}; otherwise, false
-     */
-    protected boolean matchSequence(String str, String seq, ParsePosition pos) {
-        final int idx = pos.getIndex();
-        final int inputLength = str.length();
-        final int seqLength = seq.length();
-
-        int i = idx;
-        int s = 0;
-        for (; i<inputLength && s<seqLength; ++i, ++s) {
-            if (str.codePointAt(i) != seq.codePointAt(s)) {
-                break;
-            }
-        }
-
-        if (i <= inputLength && s == seqLength) {
-            pos.setIndex(idx + seqLength);
-            return true;
-        }
-        return false;
-    }
-
-    /** Reads the string given by {@code seq} from the given position in {@code str}.
-     * Throws an IllegalArgumentException if the sequence is not found at that position.
-     * @param str the string to match against
-     * @param seq the sequence to look for in {@code str}
-     * @param pos the parse position indicating the index in {@code str}
-     *      to attempt the match
-     * @throws IllegalArgumentException if {@code str} does not contain the characters from
-     *      {@code seq} at position {@code pos}
-     */
-    protected void readSequence(String str, String seq, ParsePosition pos) throws IllegalArgumentException {
-        if (!matchSequence(str, seq, pos)) {
-            final int idx = pos.getIndex();
-            final String actualSeq = str.substring(idx, Math.min(str.length(), idx + seq.length()));
-
-            fail(String.format("expected \"%s\" but found \"%s\"", seq, actualSeq), str, pos);
-        }
-    }
-
-    /** Aborts the current parsing operation by throwing an {@link IllegalArgumentException} with an informative
-     * error message.
-     * @param msg the error message
-     * @param str the string being parsed
-     * @param pos the current parse position
-     * @throws IllegalArgumentException the exception signaling a parse failure
-     */
-    protected void fail(String msg, String str, ParsePosition pos) throws IllegalArgumentException {
-        fail(msg, str, pos, null);
-    }
-
-    /** Aborts the current parsing operation by throwing an {@link IllegalArgumentException} with an informative
-     * error message.
-     * @param msg the error message
-     * @param str the string being parsed
-     * @param pos the current parse position
-     * @param cause the original cause of the error
-     * @throws IllegalArgumentException the exception signaling a parse failure
-     */
-    protected void fail(String msg, String str, ParsePosition pos, Throwable cause) throws IllegalArgumentException {
-        String fullMsg = String.format("Failed to parse string \"%s\" at index %d: %s", str, pos.getIndex(), msg);
-
-        throw new CoordinateParseException(fullMsg, cause);
-    }
-
-    /** Exception class for errors occurring during coordinate parsing.
-     */
-    private static class CoordinateParseException extends IllegalArgumentException {
-
-        /** Serializable version identifier */
-        private static final long serialVersionUID = 1494716029613981959L;
-
-        /** Simple constructor.
-         * @param msg the exception message
-         * @param cause the exception root cause
-         */
-        CoordinateParseException(String msg, Throwable cause) {
-            super(msg, cause);
-        }
-    }
-}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/Coordinates.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/Coordinates.java
deleted file mode 100644
index c9c5fbc..0000000
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/Coordinates.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-package org.apache.commons.geometry.core.util;
-
-/** Utility class for working with coordinate tuples.
- */
-public class Coordinates {
-
-    /** Interface for classes that create objects from a single coordinate value.
-     * @param <T> The type created by this factory.
-     */
-    @FunctionalInterface
-    public interface Factory1D<T> {
-
-        /** Creates a new instance of type T from the given coordinate value.
-         * @param a the coordinate value
-         * @return a new instance of type T
-         */
-        T create(double a);
-    }
-
-    /** Interface for classes that create objects from two coordinate values.
-     * @param <T> The type created by this factory.
-     */
-    @FunctionalInterface
-    public interface Factory2D<T> {
-
-        /** Creates a new instance of type T from the given coordinate values.
-         * @param a1 the first coordinate value
-         * @param a2 the second coordinate value
-         * @return a new instance of type T
-         */
-        T create(double a1, double a2);
-    }
-
-    /** Interface for classes that create objects from three coordinate values.
-     * @param <T> The type created by this factory.
-     */
-    @FunctionalInterface
-    public interface Factory3D<T> {
-
-        /** Creates a new instance of type T from the given coordinate values.
-         * @param a1 the first coordinate value
-         * @param a2 the second coordinate value
-         * @param a3 the third coordinate value
-         * @return a new instance of type T
-         */
-        T create(double a1, double a2, double a3);
-    }
-
-    /** Private constructor. */
-    private Coordinates() {
-    }
-}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction.java
new file mode 100644
index 0000000..1b9acb6
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package org.apache.commons.geometry.core.util;
+
+/** Represents a function that accepts a single real number and returns
+ * a result.
+ * @param <T> The function return type.
+ */
+@FunctionalInterface
+public interface RealFunction<T> {
+
+    /** Apply the function and return the result.
+     * @param n the function argument
+     * @return the function result
+     */
+    T apply(double n);
+}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction2N.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction2N.java
new file mode 100644
index 0000000..20df7c7
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction2N.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+package org.apache.commons.geometry.core.util;
+
+/** Represents a function that accepts two real numbers and returns
+ * a result.
+ * @param <T> The function return type.
+ */
+@FunctionalInterface
+public interface RealFunction2N<T> {
+
+    /** Apply the function and return the result.
+     * @param n1 first function argument
+     * @param n2 second function argument
+     * @return the function result
+     */
+    T apply(double n1, double n2);
+}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction3N.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction3N.java
new file mode 100644
index 0000000..08e22e2
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/RealFunction3N.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package org.apache.commons.geometry.core.util;
+
+/** Represents a function that accepts three real numbers and returns
+ * a result.
+ * @param <T> The function return type.
+ */
+@FunctionalInterface
+public interface RealFunction3N<T> {
+
+    /** Apply the function and return the result.
+     * @param n1 first function argument
+     * @param n2 second function argument
+     * @param n3 third function argument
+     * @return the function result
+     */
+    T apply(double n1, double n2, double n3);
+}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/SimpleCoordinateFormat.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/SimpleCoordinateFormat.java
deleted file mode 100644
index 6a4eb09..0000000
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/SimpleCoordinateFormat.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.
- */
-package org.apache.commons.geometry.core.util;
-
-import java.text.ParsePosition;
-
-/** Class for performing simple formatting and parsing of coordinate tuples in common dimensions.
- */
-public class SimpleCoordinateFormat extends AbstractCoordinateParser {
-
-    /** Default coordinate separator value */
-    private static final String DEFAULT_SEPARATOR = ",";
-
-    /** Space character */
-    private static final String SPACE = " ";
-
-    /** Static instance configured with default values for working with points. */
-    private static final SimpleCoordinateFormat DEFAULT_POINT_FORMAT =
-            new SimpleCoordinateFormat(DEFAULT_SEPARATOR, "(", ")");
-
-    /** Static instance configured with default values for working with vectors. */
-    private static final SimpleCoordinateFormat DEFAULT_VECTOR_FORMAT =
-            new SimpleCoordinateFormat(DEFAULT_SEPARATOR, "{", "}");
-
-    /** Creates a new format instance with the default separator value and the given
-     * tuple prefix and suffix.
-     * @param prefix coordinate tuple prefix; may be null
-     * @param suffix coordinate tuple suffix; may be null
-     */
-    public SimpleCoordinateFormat(String prefix, String suffix) {
-        this(DEFAULT_SEPARATOR, prefix, suffix);
-    }
-
-    /** Creates a new format instance with the given separator, prefix, and suffix.
-     * @param separator string separating coordinate values
-     * @param prefix coordinate tuple prefix; may be null
-     * @param suffix coordinate tuple suffix; may be null
-     */
-    public SimpleCoordinateFormat(String separator, String prefix, String suffix) {
-        super(separator, prefix, suffix);
-    }
-
-    /** Returns a 1D coordinate tuple string with the given value.
-     * @param a coordinate value
-     * @return 1D coordinate tuple string
-     */
-    public String format(double a) {
-        StringBuilder sb = new StringBuilder();
-
-        if (getPrefix() != null) {
-            sb.append(getPrefix());
-        }
-
-        sb.append(a);
-
-        if (getSuffix() != null) {
-            sb.append(getSuffix());
-        }
-
-        return sb.toString();
-    }
-
-    /** Returns a 2D coordinate tuple string with the given values.
-     * @param a1 first coordinate value
-     * @param a2 second coordinate value
-     * @return 2D coordinate tuple string
-     */
-    public String format(double a1, double a2) {
-        StringBuilder sb = new StringBuilder();
-
-        if (getPrefix() != null) {
-            sb.append(getPrefix());
-        }
-
-        sb.append(a1);
-        sb.append(getSeparator());
-        sb.append(SPACE);
-        sb.append(a2);
-
-        if (getSuffix() != null) {
-            sb.append(getSuffix());
-        }
-
-        return sb.toString();
-    }
-
-    /** Returns a 3D coordinate tuple string with the given values.
-     * @param a1 first coordinate value
-     * @param a2 second coordinate value
-     * @param a3 third coordinate value
-     * @return 3D coordinate tuple string
-     */
-    public String format(double a1, double a2, double a3) {
-        StringBuilder sb = new StringBuilder();
-
-        if (getPrefix() != null) {
-            sb.append(getPrefix());
-        }
-
-        sb.append(a1);
-        sb.append(getSeparator());
-        sb.append(SPACE);
-        sb.append(a2);
-        sb.append(getSeparator());
-        sb.append(SPACE);
-        sb.append(a3);
-
-        if (getSuffix() != null) {
-            sb.append(getSuffix());
-        }
-
-        return sb.toString();
-    }
-
-    /** Parses the given string as a 1D coordinate tuple and passes the coordinate value to the
-     * given factory. The object created by the factory is returned.
-     * @param <T> The type created by {@code factory}
-     * @param str the string to be parsed
-     * @param factory object that will be passed the parsed coordinate value
-     * @return object created by {@code factory}
-     * @throws IllegalArgumentException if the input string format is invalid
-     */
-    public <T> T parse(String str, Coordinates.Factory1D<T> factory) throws IllegalArgumentException {
-        final ParsePosition pos = new ParsePosition(0);
-
-        readPrefix(str, pos);
-        final double v = readCoordinateValue(str, pos);
-        readSuffix(str, pos);
-        endParse(str, pos);
-
-        return factory.create(v);
-    }
-
-    /** Parses the given string as a 2D coordinate tuple and passes the coordinate values to the
-     * given factory. The object created by the factory is returned.
-     * @param <T> The type created by {@code factory}
-     * @param str the string to be parsed
-     * @param factory object that will be passed the parsed coordinate values
-     * @return object created by {@code factory}
-     * @throws IllegalArgumentException if the input string format is invalid
-     */
-    public <T> T parse(String str, Coordinates.Factory2D<T> factory) throws IllegalArgumentException {
-        final ParsePosition pos = new ParsePosition(0);
-
-        readPrefix(str, pos);
-        final double v1 = readCoordinateValue(str, pos);
-        final double v2 = readCoordinateValue(str, pos);
-        readSuffix(str, pos);
-        endParse(str, pos);
-
-        return factory.create(v1, v2);
-    }
-
-    /** Parses the given string as a 3D coordinate tuple and passes the coordinate values to the
-     * given factory. The object created by the factory is returned.
-     * @param <T> The type created by {@code factory}
-     * @param str the string to be parsed
-     * @param factory object that will be passed the parsed coordinate values
-     * @return object created by {@code factory}
-     * @throws IllegalArgumentException if the input string format is invalid
-     */
-    public <T> T parse(String str, Coordinates.Factory3D<T> factory) throws IllegalArgumentException {
-        ParsePosition pos = new ParsePosition(0);
-
-        readPrefix(str, pos);
-        final double v1 = readCoordinateValue(str, pos);
-        final double v2 = readCoordinateValue(str, pos);
-        final double v3 = readCoordinateValue(str, pos);
-        readSuffix(str, pos);
-        endParse(str, pos);
-
-        return factory.create(v1, v2, v3);
-    }
-
-    /** Returns a default instance for working with points.
-     * @return instance configured with default values for points
-     */
-    public static SimpleCoordinateFormat getPointFormat() {
-        return DEFAULT_POINT_FORMAT;
-    }
-
-    /** Returns a default instance for working with vectors.
-     * @return instance configured with default values for vectors.
-     */
-    public static SimpleCoordinateFormat getVectorFormat() {
-        return DEFAULT_VECTOR_FORMAT;
-    }
-}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/internal/SimpleTupleFormat.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/internal/SimpleTupleFormat.java
new file mode 100644
index 0000000..84641cb
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/internal/SimpleTupleFormat.java
@@ -0,0 +1,430 @@
+/*
+ * 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.
+ */
+package org.apache.commons.geometry.core.util.internal;
+
+import java.text.ParsePosition;
+
+import org.apache.commons.geometry.core.util.RealFunction;
+import org.apache.commons.geometry.core.util.RealFunction2N;
+import org.apache.commons.geometry.core.util.RealFunction3N;
+
+/** Class for performing simple formatting and parsing of real number tuples.
+ */
+public class SimpleTupleFormat {
+
+    /** Default value separator string. */
+    private static final String DEFAULT_SEPARATOR = ",";
+
+    /** Space character */
+    private static final String SPACE = " ";
+
+    /** Static instance configured with default values. Tuples in this format
+     * are enclosed by parentheses and separated by commas.
+     */
+    private static final SimpleTupleFormat DEFAULT_INSTANCE =
+            new SimpleTupleFormat(",", "(", ")");
+
+    /** String separating tuple values */
+    private final String separator;
+
+    /** String used to signal the start of a tuple; may be null */
+    private final String prefix;
+
+    /** String used to signal the end of a tuple; may be null */
+    private final String suffix;
+
+    /** Constructs a new instance with the default string separator (a comma)
+     * and the given prefix and suffix.
+     * @param prefix String used to signal the start of a tuple; if null, no
+     *      string is expected at the start of the tuple
+     * @param suffix String used to signal the end of a tuple; if null, no
+     *      string is expected at the end of the tuple
+     */
+    public SimpleTupleFormat(String prefix, String suffix) {
+        this(DEFAULT_SEPARATOR, prefix, suffix);
+    }
+
+    /** Simple constructor.
+     * @param separator String used to separate tuple values; must not be null.
+     * @param prefix String used to signal the start of a tuple; if null, no
+     *      string is expected at the start of the tuple
+     * @param suffix String used to signal the end of a tuple; if null, no
+     *      string is expected at the end of the tuple
+     */
+    protected SimpleTupleFormat(String separator, String prefix, String suffix) {
+        this.separator = separator;
+        this.prefix = prefix;
+        this.suffix = suffix;
+    }
+
+    /** Return the string used to separate tuple values.
+     * @return the value separator string
+     */
+    public String getSeparator() {
+        return separator;
+    }
+
+    /** Return the string used to signal the start of a tuple. This value may be null.
+     * @return the string used to begin each tuple or null
+     */
+    public String getPrefix() {
+        return prefix;
+    }
+
+    /** Returns the string used to signal the end of a tuple. This value may be null.
+     * @return the string used to end each tuple or null
+     */
+    public String getSuffix() {
+        return suffix;
+    }
+
+    /** Return a tuple string with the given value.
+     * @param a value
+     * @return 1-tuple string
+     */
+    public String format(double a) {
+        StringBuilder sb = new StringBuilder();
+
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+
+        sb.append(a);
+
+        if (suffix != null) {
+            sb.append(suffix);
+        }
+
+        return sb.toString();
+    }
+
+    /** Return a tuple string with the given values.
+     * @param a1 first value
+     * @param a2 second value
+     * @return 2-tuple string
+     */
+    public String format(double a1, double a2) {
+        StringBuilder sb = new StringBuilder();
+
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+
+        sb.append(a1);
+        sb.append(separator);
+        sb.append(SPACE);
+        sb.append(a2);
+
+        if (suffix != null) {
+            sb.append(suffix);
+        }
+
+        return sb.toString();
+    }
+
+    /** Return a tuple string with the given values.
+     * @param a1 first value
+     * @param a2 second value
+     * @param a3 third value
+     * @return 3-tuple string
+     */
+    public String format(double a1, double a2, double a3) {
+        StringBuilder sb = new StringBuilder();
+
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+
+        sb.append(a1);
+        sb.append(separator);
+        sb.append(SPACE);
+        sb.append(a2);
+        sb.append(separator);
+        sb.append(SPACE);
+        sb.append(a3);
+
+        if (suffix != null) {
+            sb.append(suffix);
+        }
+
+        return sb.toString();
+    }
+
+    /** Parse the given string as a 1-tuple and passes the tuple values to the
+     * given function. The function output is returned.
+     * @param <T> function return type
+     * @param str the string to be parsed
+     * @param fn function that will be passed the parsed tuple values
+     * @return object returned by {@code fn}
+     * @throws IllegalArgumentException if the input string format is invalid
+     */
+    public <T> T parse(String str, RealFunction<T> factory) throws IllegalArgumentException {
+        final ParsePosition pos = new ParsePosition(0);
+
+        readPrefix(str, pos);
+        final double v = readTupleValue(str, pos);
+        readSuffix(str, pos);
+        endParse(str, pos);
+
+        return factory.apply(v);
+    }
+
+    /** Parse the given string as a 2-tuple and passes the tuple values to the
+     * given function. The function output is returned.
+     * @param <T> function return type
+     * @param str the string to be parsed
+     * @param fn function that will be passed the parsed tuple values
+     * @return object returned by {@code fn}
+     * @throws IllegalArgumentException if the input string format is invalid
+     */
+    public <T> T parse(String str, RealFunction2N<T> factory) throws IllegalArgumentException {
+        final ParsePosition pos = new ParsePosition(0);
+
+        readPrefix(str, pos);
+        final double v1 = readTupleValue(str, pos);
+        final double v2 = readTupleValue(str, pos);
+        readSuffix(str, pos);
+        endParse(str, pos);
+
+        return factory.apply(v1, v2);
+    }
+
+    /** Parse the given string as a 3-tuple and passes the parsed values to the
+     * given function. The function output is returned.
+     * @param <T> function return type
+     * @param str the string to be parsed
+     * @param fn function that will be passed the parsed tuple values
+     * @return object returned by {@code fn}
+     * @throws IllegalArgumentException if the input string format is invalid
+     */
+    public <T> T parse(String str, RealFunction3N<T> fn) throws IllegalArgumentException {
+        ParsePosition pos = new ParsePosition(0);
+
+        readPrefix(str, pos);
+        final double v1 = readTupleValue(str, pos);
+        final double v2 = readTupleValue(str, pos);
+        final double v3 = readTupleValue(str, pos);
+        readSuffix(str, pos);
+        endParse(str, pos);
+
+        return fn.apply(v1, v2, v3);
+    }
+
+    /** Read the configured prefix from the current position in the given string, ignoring any preceding
+     * whitespace, and advance the parsing position past the prefix sequence. An exception is thrown if the
+     * prefix is not found. Does nothing if the prefix is null.
+     * @param str the string being parsed
+     * @param pos the current parsing position
+     * @throws IllegalArgumentException if the configured prefix is not null and is not found at the current
+     *      parsing position, ignoring preceding whitespace
+     */
+    private void readPrefix(String str, ParsePosition pos) throws IllegalArgumentException {
+        if (prefix != null) {
+            consumeWhitespace(str, pos);
+            readSequence(str, prefix, pos);
+        }
+    }
+
+    /** Read and return a tuple value from the current position in the given string. An exception is thrown if a
+     * valid number is not found. The parsing position is advanced past the parsed number and any trailing separator.
+     * @param str the string being parsed
+     * @param pos the current parsing position
+     * @return the tuple value
+     * @throws IllegalArgumentException if the configured prefix is not null and is not found at the current
+     *      parsing position, ignoring preceding whitespace
+     */
+    private double readTupleValue(String str, ParsePosition pos) throws IllegalArgumentException {
+        final int startIdx = pos.getIndex();
+
+        int endIdx = str.indexOf(separator, startIdx);
+        if (endIdx < 0) {
+            if (suffix != null) {
+                endIdx = str.indexOf(suffix, startIdx);
+            }
+
+            if (endIdx < 0) {
+                endIdx = str.length();
+            }
+        }
+
+        String substr = str.substring(startIdx, endIdx);
+        try {
+            double value = Double.parseDouble(substr);
+
+            // advance the position and move past any terminating separator
+            pos.setIndex(endIdx);
+            matchSequence(str, separator, pos);
+
+            return value;
+        }
+        catch (NumberFormatException exc) {
+            fail(String.format("unable to parse number from string \"%s\"", substr), str, pos, exc);
+            return 0.0; // for the compiler
+        }
+    }
+
+    /** Read the configured suffix from the current position in the given string, ignoring any preceding
+     * whitespace, and advance the parsing position past the suffix sequence. An exception is thrown if the
+     * suffix is not found. Does nothing if the suffix is null.
+     * @param str the string being parsed
+     * @param pos the current parsing position
+     * @throws IllegalArgumentException if the configured suffix is not null and is not found at the current
+     *      parsing position, ignoring preceding whitespace
+     */
+    private void readSuffix(String str, ParsePosition pos) throws IllegalArgumentException {
+        if (suffix != null) {
+            consumeWhitespace(str, pos);
+            readSequence(str, suffix, pos);
+        }
+    }
+
+    /** End a parse operation by ensuring that all non-whitespace characters in the string have been parsed. An
+     * exception is thrown if extra content is found.
+     * @param str the string being parsed
+     * @param pos the current parsing position
+     * @throws IllegalArgumentException if extra non-whitespace content is found past the current parsing position
+     */
+    private void endParse(String str, ParsePosition pos) throws IllegalArgumentException {
+        consumeWhitespace(str, pos);
+        if (pos.getIndex() != str.length()) {
+            fail("unexpected content", str, pos);
+        }
+    }
+
+    /** Advance {@code pos} past any whitespace characters in {@code str},
+     * starting at the current parse position index.
+     * @param str the input string
+     * @param pos the current parse position
+     */
+    private void consumeWhitespace(final String str, final ParsePosition pos) {
+        int idx = pos.getIndex();
+        final int len = str.length();
+
+        for (; idx<len; ++idx) {
+            if (!Character.isWhitespace(str.codePointAt(idx))) {
+                break;
+            }
+        }
+
+        pos.setIndex(idx);
+    }
+
+    /** Return a boolean indicating whether or not the input string {@code str}
+     * contains the string {@code seq} at the given parse index. If the match succeeds,
+     * the index of {@code pos} is moved to the first character after the match. If
+     * the match does not succeed, the parse position is left unchanged.
+     * @param str the string to match against
+     * @param seq the sequence to look for in {@code str}
+     * @param pos the parse position indicating the index in {@code str}
+     *      to attempt the match
+     * @return true if {@code str} contains exactly the same characters as {@code seq}
+     *      at {@code pos}; otherwise, false
+     */
+    private boolean matchSequence(final String str, final String seq, final ParsePosition pos) {
+        final int idx = pos.getIndex();
+        final int inputLength = str.length();
+        final int seqLength = seq.length();
+
+        int i = idx;
+        int s = 0;
+        for (; i<inputLength && s<seqLength; ++i, ++s) {
+            if (str.codePointAt(i) != seq.codePointAt(s)) {
+                break;
+            }
+        }
+
+        if (i <= inputLength && s == seqLength) {
+            pos.setIndex(idx + seqLength);
+            return true;
+        }
+        return false;
+    }
+
+    /** Read the string given by {@code seq} from the given position in {@code str}.
+     * Throws an IllegalArgumentException if the sequence is not found at that position.
+     * @param str the string to match against
+     * @param seq the sequence to look for in {@code str}
+     * @param pos the parse position indicating the index in {@code str}
+     *      to attempt the match
+     * @throws IllegalArgumentException if {@code str} does not contain the characters from
+     *      {@code seq} at position {@code pos}
+     */
+    private void readSequence(String str, String seq, ParsePosition pos) throws IllegalArgumentException {
+        if (!matchSequence(str, seq, pos)) {
+            final int idx = pos.getIndex();
+            final String actualSeq = str.substring(idx, Math.min(str.length(), idx + seq.length()));
+
+            fail(String.format("expected \"%s\" but found \"%s\"", seq, actualSeq), str, pos);
+        }
+    }
+
+    /** Abort the current parsing operation by throwing an {@link IllegalArgumentException} with an informative
+     * error message.
+     * @param msg the error message
+     * @param str the string being parsed
+     * @param pos the current parse position
+     * @throws IllegalArgumentException the exception signaling a parse failure
+     */
+    private void fail(String msg, String str, ParsePosition pos) throws IllegalArgumentException {
+        fail(msg, str, pos, null);
+    }
+
+    /** Abort the current parsing operation by throwing an {@link IllegalArgumentException} with an informative
+     * error message.
+     * @param msg the error message
+     * @param str the string being parsed
+     * @param pos the current parse position
+     * @param cause the original cause of the error
+     * @throws IllegalArgumentException the exception signaling a parse failure
+     */
+    private void fail(String msg, String str, ParsePosition pos, Throwable cause) throws IllegalArgumentException {
+        String fullMsg = String.format("Failed to parse string \"%s\" at index %d: %s", str, pos.getIndex(), msg);
+
+        throw new TupleParseException(fullMsg, cause);
+    }
+
+    /** Return an instance configured with default values. Tuples in this format
+     * are enclosed by parentheses and separated by commas.
+     *
+     * Ex:
+     * <pre>
+     * "(1.0)"
+     * "(1.0, 2.0)"
+     * "(1.0, 2.0, 3.0)"
+     * </pre>
+     * @return instance configured with default values
+     */
+    public static SimpleTupleFormat getDefault() {
+        return DEFAULT_INSTANCE;
+    }
+
+    /** Exception class for errors occurring during tuple parsing.
+     */
+    private static class TupleParseException extends IllegalArgumentException {
+
+        /** Serializable version identifier */
+        private static final long serialVersionUID = 20180629;
+
+        /** Simple constructor.
+         * @param msg the exception message
+         * @param cause the exception root cause
+         */
+        TupleParseException(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+}
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/internal/package-info.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/internal/package-info.java
new file mode 100644
index 0000000..547fc6b
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/internal/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+/**
+ *
+ * <p>
+ * This package contains geometry utilities intended for internal use only.
+ * No guarantees are made for the stability of the contained APIs.
+ * </p>
+ */
+package org.apache.commons.geometry.core.util.internal;
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/SimpleCoordinateFormatTest.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/internal/SimpleTupleFormatTest.java
similarity index 80%
rename from commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/SimpleCoordinateFormatTest.java
rename to commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/internal/SimpleTupleFormatTest.java
index 23202d7..a3e554b 100644
--- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/SimpleCoordinateFormatTest.java
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/internal/SimpleTupleFormatTest.java
@@ -14,22 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.core.util;
+package org.apache.commons.geometry.core.util.internal;
 
+import org.apache.commons.geometry.core.util.RealFunction;
+import org.apache.commons.geometry.core.util.RealFunction2N;
+import org.apache.commons.geometry.core.util.RealFunction3N;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class SimpleCoordinateFormatTest {
+public class SimpleTupleFormatTest {
 
     private static final double EPS = 1e-10;
 
     private static final String OPEN_PAREN = "(";
     private static final String CLOSE_PAREN = ")";
 
-    private static Coordinates.Factory1D<Stub1D> FACTORY_1D = new Coordinates.Factory1D<Stub1D>() {
+    private static RealFunction<Stub1D> FACTORY_1D = new RealFunction<Stub1D>() {
 
         @Override
-        public Stub1D create(double v) {
+        public Stub1D apply(double v) {
             Stub1D result = new Stub1D();
             result.v = v;
 
@@ -37,10 +40,10 @@ public class SimpleCoordinateFormatTest {
         }
     };
 
-    private static Coordinates.Factory2D<Stub2D> FACTORY_2D = new Coordinates.Factory2D<Stub2D>() {
+    private static RealFunction2N<Stub2D> FACTORY_2D = new RealFunction2N<Stub2D>() {
 
         @Override
-        public Stub2D create(double v1, double v2) {
+        public Stub2D apply(double v1, double v2) {
             Stub2D result = new Stub2D();
             result.v1 = v1;
             result.v2 = v2;
@@ -49,10 +52,10 @@ public class SimpleCoordinateFormatTest {
         }
     };
 
-    private static Coordinates.Factory3D<Stub3D> FACTORY_3D = new Coordinates.Factory3D<Stub3D>() {
+    private static RealFunction3N<Stub3D> FACTORY_3D = new RealFunction3N<Stub3D>() {
 
         @Override
-        public Stub3D create(double v1, double v2, double v3) {
+        public Stub3D apply(double v1, double v2, double v3) {
             Stub3D result = new Stub3D();
             result.v1 = v1;
             result.v2 = v2;
@@ -65,7 +68,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testConstructor() {
         // act
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat("|", "{", "}");
+        SimpleTupleFormat formatter = new SimpleTupleFormat("|", "{", "}");
 
         // assert
         Assert.assertEquals("|", formatter.getSeparator());
@@ -76,7 +79,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testConstructor_defaultSeparator() {
         // act
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat("{", "}");
+        SimpleTupleFormat formatter = new SimpleTupleFormat("{", "}");
 
         // assert
         Assert.assertEquals(",", formatter.getSeparator());
@@ -87,7 +90,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat1D() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         Assert.assertEquals("(1.0)", formatter.format(1.0));
@@ -100,7 +103,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat1D_noPrefixSuffix() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(null, null);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(null, null);
 
         // act/assert
         Assert.assertEquals("1.0", formatter.format(1.0));
@@ -113,7 +116,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat2D() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         Assert.assertEquals("(1.0, -1.0)", formatter.format(1.0, -1.0));
@@ -125,7 +128,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat2D_noPrefixSuffix() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(null, null);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(null, null);
 
         // act/assert
         Assert.assertEquals("1.0, -1.0", formatter.format(1.0, -1.0));
@@ -137,7 +140,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat3D() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         Assert.assertEquals("(1.0, 0.0, -1.0)", formatter.format(1.0, 0.0, -1.0));
@@ -148,7 +151,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat3D_noPrefixSuffix() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(null, null);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(null, null);
 
         // act/assert
         Assert.assertEquals("1.0, 0.0, -1.0", formatter.format(1.0, 0.0, -1.0));
@@ -159,7 +162,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testFormat_longTokens() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat("||", "<<", ">>");
+        SimpleTupleFormat formatter = new SimpleTupleFormat("||", "<<", ">>");
 
         // act/assert
         Assert.assertEquals("<<1.0>>", formatter.format(1.0));
@@ -170,7 +173,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse1D() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         checkParse1D(formatter, "(1)", 1.0);
@@ -196,7 +199,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse1D_noPrefixSuffix() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(null, null);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(null, null);
 
         // act/assert
         checkParse1D(formatter, "1", 1.0);
@@ -222,7 +225,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse1D_failure() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         checkParse1DFailure(formatter, "", "index 0: expected \"(\" but found \"\"");
@@ -236,7 +239,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse2D() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         checkParse2D(formatter, "(1,-2)", 1.0, -2.0);
@@ -260,7 +263,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse2D_noPrefixSuffix() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(null, null);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(null, null);
 
         // act/assert
         checkParse2D(formatter, "1,-2", 1.0, -2.0);
@@ -284,7 +287,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse2D_failure() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         checkParse2DFailure(formatter, "", "index 0: expected \"(\" but found \"\"");
@@ -298,7 +301,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse3D() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         checkParse3D(formatter, "(1,-2,3)", 1.0, -2.0, 3.0);
@@ -321,7 +324,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse3D_noPrefixSuffix() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(null, null);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(null, null);
 
         // act/assert
         checkParse3D(formatter, "1,-2,3", 1.0, -2.0, 3.0);
@@ -344,7 +347,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse3D_failure() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat(OPEN_PAREN, CLOSE_PAREN);
+        SimpleTupleFormat formatter = new SimpleTupleFormat(OPEN_PAREN, CLOSE_PAREN);
 
         // act/assert
         checkParse3DFailure(formatter, "", "index 0: expected \"(\" but found \"\"");
@@ -358,7 +361,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse_longTokens() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat("||", "<<", ">>");
+        SimpleTupleFormat formatter = new SimpleTupleFormat("||", "<<", ">>");
 
         // act/assert
         checkParse1D(formatter, "<<1.0>>", 1.0);
@@ -369,7 +372,7 @@ public class SimpleCoordinateFormatTest {
     @Test
     public void testParse_longTokens_failure() {
         // arrange
-        SimpleCoordinateFormat formatter = new SimpleCoordinateFormat("||", "<<", ">>");
+        SimpleTupleFormat formatter = new SimpleTupleFormat("||", "<<", ">>");
 
         // act/assert
         checkParse1DFailure(formatter, "<", "index 0: expected \"<<\" but found \"<\"");
@@ -379,9 +382,9 @@ public class SimpleCoordinateFormatTest {
     }
 
     @Test
-    public void testDefaultPointFormat() {
+    public void testDefaultInstance() {
         // act
-        SimpleCoordinateFormat formatter = SimpleCoordinateFormat.getPointFormat();
+        SimpleTupleFormat formatter = SimpleTupleFormat.getDefault();
 
         // assert
         Assert.assertEquals(",", formatter.getSeparator());
@@ -391,26 +394,13 @@ public class SimpleCoordinateFormatTest {
         Assert.assertEquals("(1.0, 2.0)", formatter.format(1, 2));
     }
 
-    @Test
-    public void testDefaultVectorFormat() {
-        // act
-        SimpleCoordinateFormat formatter = SimpleCoordinateFormat.getVectorFormat();
-
-        // assert
-        Assert.assertEquals(",", formatter.getSeparator());
-        Assert.assertEquals("{", formatter.getPrefix());
-        Assert.assertEquals("}", formatter.getSuffix());
-
-        Assert.assertEquals("{1.0, 2.0}", formatter.format(1, 2));
-    }
-
-    private void checkParse1D(SimpleCoordinateFormat formatter, String str, double v) {
+    private void checkParse1D(SimpleTupleFormat formatter, String str, double v) {
         Stub1D result = formatter.parse(str, FACTORY_1D);
 
         Assert.assertEquals(v, result.v, EPS);
     }
 
-    private void checkParse1DFailure(SimpleCoordinateFormat formatter, String str, String msgSubstr) {
+    private void checkParse1DFailure(SimpleTupleFormat formatter, String str, String msgSubstr) {
         try {
             formatter.parse(str, FACTORY_1D);
             Assert.fail("Operation should have failed");
@@ -422,14 +412,14 @@ public class SimpleCoordinateFormatTest {
         }
     }
 
-    private void checkParse2D(SimpleCoordinateFormat formatter, String str, double v1, double v2) {
+    private void checkParse2D(SimpleTupleFormat formatter, String str, double v1, double v2) {
         Stub2D result = formatter.parse(str, FACTORY_2D);
 
         Assert.assertEquals(v1, result.v1, EPS);
         Assert.assertEquals(v2, result.v2, EPS);
     }
 
-    private void checkParse2DFailure(SimpleCoordinateFormat formatter, String str, String msgSubstr) {
+    private void checkParse2DFailure(SimpleTupleFormat formatter, String str, String msgSubstr) {
         try {
             formatter.parse(str, FACTORY_2D);
             Assert.fail("Operation should have failed");
@@ -441,7 +431,7 @@ public class SimpleCoordinateFormatTest {
         }
     }
 
-    private void checkParse3D(SimpleCoordinateFormat formatter, String str, double v1, double v2, double v3) {
+    private void checkParse3D(SimpleTupleFormat formatter, String str, double v1, double v2, double v3) {
         Stub3D result = formatter.parse(str, FACTORY_3D);
 
         Assert.assertEquals(v1, result.v1, EPS);
@@ -449,7 +439,7 @@ public class SimpleCoordinateFormatTest {
         Assert.assertEquals(v3, result.v3, EPS);
     }
 
-    private void checkParse3DFailure(SimpleCoordinateFormat formatter, String str, String msgSubstr) {
+    private void checkParse3DFailure(SimpleTupleFormat formatter, String str, String msgSubstr) {
         try {
             formatter.parse(str, FACTORY_3D);
             Assert.fail("Operation should have failed");
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java
index 8d9b800..19ea67f 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java
@@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.oned;
 import java.io.Serializable;
 
 import org.apache.commons.geometry.core.Spatial;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 
 /** This class represents a Cartesian coordinate value in
  * one-dimensional Euclidean space.
@@ -64,4 +65,10 @@ public abstract class Cartesian1D implements Spatial, Serializable {
     public boolean isInfinite() {
         return !isNaN() && Double.isInfinite(x);
     }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return SimpleTupleFormat.getDefault().format(getX());
+    }
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
index 3715c69..870ac6e 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
@@ -16,8 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.EuclideanPoint;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
@@ -49,12 +49,12 @@ public final class Point1D extends Cartesian1D implements EuclideanPoint<Point1D
     private static final long serialVersionUID = 7556674948671647925L;
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory1D<Point1D> FACTORY = new Coordinates.Factory1D<Point1D>() {
+    private static RealFunction<Point1D> FACTORY = new RealFunction<Point1D>() {
 
         /** {@inheritDoc} */
         @Override
-        public Point1D create(double a) {
-            return new Point1D(a);
+        public Point1D apply(double n) {
+            return new Point1D(n);
         }
     };
 
@@ -145,12 +145,6 @@ public final class Point1D extends Cartesian1D implements EuclideanPoint<Point1D
         return false;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getX());
-    }
-
     /** Returns a point with the given coordinate value.
      * @param x point coordinate
      * @return point instance
@@ -174,13 +168,13 @@ public final class Point1D extends Cartesian1D implements EuclideanPoint<Point1D
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static Point1D parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getPointFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new point instances.
      * @return point factory instance
      */
-    public static Coordinates.Factory1D<Point1D> getFactory() {
+    public static RealFunction<Point1D> getFactory() {
         return FACTORY;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
index 0aab089..d9fd0c2 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
@@ -16,8 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
@@ -49,12 +49,12 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point
     private static final long serialVersionUID = 1582116020164328846L;
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory1D<Vector1D> FACTORY = new Coordinates.Factory1D<Vector1D>() {
+    private static RealFunction<Vector1D> FACTORY = new RealFunction<Vector1D>() {
 
         /** {@inheritDoc} */
         @Override
-        public Vector1D create(double a) {
-            return new Vector1D(a);
+        public Vector1D apply(double n) {
+            return new Vector1D(n);
         }
     };
 
@@ -229,12 +229,6 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point
         return false;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return SimpleCoordinateFormat.getVectorFormat().format(getX());
-    }
-
     /** Returns a vector with the given coordinate value.
      * @param x vector coordinate
      * @return vector instance
@@ -258,13 +252,13 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static Vector1D parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getVectorFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new vector instances.
      * @return vector factory instance
      */
-    public static Coordinates.Factory1D<Vector1D> getFactory() {
+    public static RealFunction<Vector1D> getFactory() {
         return FACTORY;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java
index 6b619ac..8d680e9 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java
@@ -20,6 +20,7 @@ package org.apache.commons.geometry.euclidean.threed;
 import java.io.Serializable;
 
 import org.apache.commons.geometry.core.Spatial;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 
 /** This class represents a Cartesian coordinate value in
  * three-dimensional Euclidean space.
@@ -95,6 +96,12 @@ public abstract class Cartesian3D implements Spatial, Serializable {
         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return SimpleTupleFormat.getDefault().format(getX(), getY(), getZ());
+    }
+
     /** Returns the Euclidean distance from this set of coordinates to the given coordinates.
      * @param other coordinates to compute the distance to.
      * @return Euclidean distance value
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
index f257425..c9841ac 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
@@ -17,8 +17,8 @@
 
 package org.apache.commons.geometry.euclidean.threed;
 
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction3N;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.EuclideanPoint;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
@@ -47,12 +47,12 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
     private static final long serialVersionUID = 1313493323784566947L;
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory3D<Point3D> FACTORY = new Coordinates.Factory3D<Point3D>() {
+    private static RealFunction3N<Point3D> FACTORY = new RealFunction3N<Point3D>() {
 
         /** {@inheritDoc} */
         @Override
-        public Point3D create(double a1, double a2, double a3) {
-            return new Point3D(a1, a2, a3);
+        public Point3D apply(double n1, double n2, double n3) {
+            return new Point3D(n1, n2, n3);
         }
     };
 
@@ -154,12 +154,6 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
         return false;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getX(), getY(), getZ());
-    }
-
     /** Returns a point with the given coordinate values
      * @param x abscissa (first coordinate value)
      * @param y ordinate (second coordinate value)
@@ -197,13 +191,13 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static Point3D parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getPointFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new point instances.
      * @return point factory instance
      */
-    public static Coordinates.Factory3D<Point3D> getFactory() {
+    public static RealFunction3N<Point3D> getFactory() {
         return FACTORY;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
index 9d1f258..5e9d952 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
@@ -16,8 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction3N;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
@@ -67,12 +67,12 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point
     private static final String ZERO_NORM_MSG = "Norm is zero";
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory3D<Vector3D> FACTORY = new Coordinates.Factory3D<Vector3D>() {
+    private static RealFunction3N<Vector3D> FACTORY = new RealFunction3N<Vector3D>() {
 
         /** {@inheritDoc} */
         @Override
-        public Vector3D create(double a1, double a2, double a3) {
-            return new Vector3D(a1, a2, a3);
+        public Vector3D apply(double n1, double n2, double n3) {
+            return new Vector3D(n1, n2, n3);
         }
     };
 
@@ -382,12 +382,6 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point
         return false;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return SimpleCoordinateFormat.getVectorFormat().format(getX(), getY(), getZ());
-    }
-
     /** Computes the dot product between to vectors. This method simply
      * calls {@code v1.dotProduct(v2)}.
      * @param v1 first vector
@@ -477,13 +471,13 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static Vector3D parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getVectorFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new vector instances.
      * @return vector factory instance
      */
-    public static Coordinates.Factory3D<Vector3D> getFactory() {
+    public static RealFunction3N<Vector3D> getFactory() {
         return FACTORY;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java
index d4c69b6..8ddb6a0 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java
@@ -20,6 +20,7 @@ package org.apache.commons.geometry.euclidean.twod;
 import java.io.Serializable;
 
 import org.apache.commons.geometry.core.Spatial;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 
 /** This class represents a set of Cartesian coordinates in
  * two-dimensional Euclidean space.
@@ -84,6 +85,12 @@ public abstract class Cartesian2D implements Spatial, Serializable {
         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return SimpleTupleFormat.getDefault().format(getX(), getY());
+    }
+
     /** Returns the Euclidean distance from this value to the given value.
      * @param other the set of coordinates to compute the distance to
      * @return Euclidean distance
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
index c32777a..0d2af49 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
@@ -16,8 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction2N;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.EuclideanPoint;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
@@ -46,12 +46,12 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
     private static final long serialVersionUID = 266938651998679754L;
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory2D<Point2D> FACTORY = new Coordinates.Factory2D<Point2D>() {
+    private static RealFunction2N<Point2D> FACTORY = new RealFunction2N<Point2D>() {
 
         /** {@inheritDoc} */
         @Override
-        public Point2D create(double a1, double a2) {
-            return new Point2D(a1, a2);
+        public Point2D apply(double n1, double n2) {
+            return new Point2D(n1, n2);
         }
     };
 
@@ -143,12 +143,6 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
         return false;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getX(), getY());
-    }
-
     /** Returns a point with the given coordinate values
      * @param x abscissa (first coordinate value)
      * @param y ordinate (second coordinate value)
@@ -185,13 +179,13 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static Point2D parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getPointFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new point instances.
      * @return point factory instance
      */
-    public static Coordinates.Factory2D<Point2D> getFactory() {
+    public static RealFunction2N<Point2D> getFactory() {
         return FACTORY;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
index ac8e380..42dc10d 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
@@ -16,8 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction2N;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
@@ -61,12 +61,12 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
     private static final String ZERO_NORM_MSG = "Norm is zero";
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory2D<Vector2D> FACTORY = new Coordinates.Factory2D<Vector2D>() {
+    private static RealFunction2N<Vector2D> FACTORY = new RealFunction2N<Vector2D>() {
 
         /** {@inheritDoc} */
         @Override
-        public Vector2D create(double a1, double a2) {
-            return new Vector2D(a1, a2);
+        public Vector2D apply(double n1, double n2) {
+            return new Vector2D(n1, n2);
         }
     };
 
@@ -320,12 +320,6 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
         return false;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return SimpleCoordinateFormat.getVectorFormat().format(getX(), getY());
-    }
-
     /** Computes the dot product between to vectors. This method simply
      * calls {@code v1.dotProduct(v2)}.
      * @param v1 first vector
@@ -384,13 +378,13 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static Vector2D parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getVectorFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new vector instances.
      * @return vector factory instance
      */
-    public static Coordinates.Factory2D<Vector2D> getFactory() {
+    public static RealFunction2N<Vector2D> getFactory() {
         return FACTORY;
     }
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java
index 835a368..afd6ca8 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
+import java.util.regex.Pattern;
+
 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
 import org.junit.Assert;
 import org.junit.Test;
@@ -64,6 +66,20 @@ public class Cartesian1DTest {
         Assert.assertFalse(new StubCartesian1D(Double.NaN).isInfinite());
     }
 
+    @Test
+    public void testToString() {
+        // arrange
+        StubCartesian1D c = new StubCartesian1D(1);
+        Pattern pattern = Pattern.compile("\\(1.{0,2}\\)");
+
+        // act
+        String str = c.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
     private static class StubCartesian1D extends Cartesian1D {
         private static final long serialVersionUID = 1L;
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
index bcde081..2ac7379 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
@@ -19,7 +19,7 @@ package org.apache.commons.geometry.euclidean.oned;
 
 import java.util.regex.Pattern;
 
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -231,11 +231,11 @@ public class Point1DTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory1D<Point1D> factory = Point1D.getFactory();
+        RealFunction<Point1D> factory = Point1D.getFactory();
 
         // assert
-        checkPoint(factory.create(1), 1);
-        checkPoint(factory.create(-1), -1);
+        checkPoint(factory.apply(1), 1);
+        checkPoint(factory.apply(-1), -1);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
index 7184476..9e81737 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
@@ -2,7 +2,7 @@ package org.apache.commons.geometry.euclidean.oned;
 
 import java.util.regex.Pattern;
 
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -281,7 +281,7 @@ public class Vector1DTest {
     public void testToString() {
         // arrange
         Vector1D v = Vector1D.of(3);
-        Pattern pattern = Pattern.compile("\\{3.{0,2}\\}");
+        Pattern pattern = Pattern.compile("\\(3.{0,2}\\)");
 
         // act
         String str = v.toString();
@@ -294,13 +294,13 @@ public class Vector1DTest {
     @Test
     public void testParse() {
         // act/assert
-        checkVector(Vector1D.parse("{1}"), 1);
-        checkVector(Vector1D.parse("{-1}"), -1);
+        checkVector(Vector1D.parse("(1)"), 1);
+        checkVector(Vector1D.parse("(-1)"), -1);
 
-        checkVector(Vector1D.parse("{0.01}"), 1e-2);
-        checkVector(Vector1D.parse("{-1e-3}"), -1e-3);
+        checkVector(Vector1D.parse("(0.01)"), 1e-2);
+        checkVector(Vector1D.parse("(-1e-3)"), -1e-3);
 
-        checkVector(Vector1D.parse("{NaN}"), Double.NaN);
+        checkVector(Vector1D.parse("(NaN)"), Double.NaN);
 
         checkVector(Vector1D.parse(Vector1D.ZERO.toString()), 0);
         checkVector(Vector1D.parse(Vector1D.ONE.toString()), 1);
@@ -339,11 +339,11 @@ public class Vector1DTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory1D<Vector1D> factory = Vector1D.getFactory();
+        RealFunction<Vector1D> factory = Vector1D.getFactory();
 
         // assert
-        checkVector(factory.create(1), 1);
-        checkVector(factory.create(-1), -1);
+        checkVector(factory.apply(1), 1);
+        checkVector(factory.apply(-1), -1);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
index 4e6205d..aadfe73 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
@@ -1,5 +1,7 @@
 package org.apache.commons.geometry.euclidean.threed;
 
+import java.util.regex.Pattern;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -73,6 +75,20 @@ public class Cartesian3DTest {
         Assert.assertFalse(new StubCartesian3D(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
     }
 
+    @Test
+    public void testToString() {
+        // arrange
+        StubCartesian3D c = new StubCartesian3D(1, 2, 3);
+        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
+
+        // act
+        String str = c.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
     private static class StubCartesian3D extends Cartesian3D {
         private static final long serialVersionUID = 1L;
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
index d20d918..d2959dc 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
@@ -18,7 +18,7 @@ package org.apache.commons.geometry.euclidean.threed;
 
 import java.util.regex.Pattern;
 
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction3N;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -245,11 +245,11 @@ public class Point3DTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
+        RealFunction3N<Point3D> factory = Point3D.getFactory();
 
         // assert
-        checkPoint(factory.create(1, 2, 3), 1, 2, 3);
-        checkPoint(factory.create(-1, -2, -3), -1, -2, -3);
+        checkPoint(factory.apply(1, 2, 3), 1, 2, 3);
+        checkPoint(factory.apply(-1, -2, -3), -1, -2, -3);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
index c9b3cf9..17e6a36 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
@@ -20,7 +20,7 @@ package org.apache.commons.geometry.euclidean.threed;
 import java.util.regex.Pattern;
 
 import org.apache.commons.geometry.core.Geometry;
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction3N;
 import org.apache.commons.numbers.core.Precision;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.simple.RandomSource;
@@ -619,7 +619,7 @@ public class Vector3DTest {
     public void testToString() {
         // arrange
         Vector3D v = Vector3D.of(1, 2, 3);
-        Pattern pattern = Pattern.compile("\\{1.{0,2}, 2.{0,2}, 3.{0,2}\\}");
+        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
 
         // act
         String str = v.toString();
@@ -632,12 +632,12 @@ public class Vector3DTest {
     @Test
     public void testParse() {
         // act/assert
-        checkVector(Vector3D.parse("{1, 2, 3}"), 1, 2, 3);
-        checkVector(Vector3D.parse("{-1, -2, -3}"), -1, -2, -3);
+        checkVector(Vector3D.parse("(1, 2, 3)"), 1, 2, 3);
+        checkVector(Vector3D.parse("(-1, -2, -3)"), -1, -2, -3);
 
-        checkVector(Vector3D.parse("{0.01, -1e-3, 0}"), 1e-2, -1e-3, 0);
+        checkVector(Vector3D.parse("(0.01, -1e-3, 0)"), 1e-2, -1e-3, 0);
 
-        checkVector(Vector3D.parse("{NaN, -Infinity, Infinity}"), Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
+        checkVector(Vector3D.parse("(NaN, -Infinity, Infinity)"), Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
 
         checkVector(Vector3D.parse(Vector3D.ZERO.toString()), 0, 0, 0);
         checkVector(Vector3D.parse(Vector3D.MINUS_X.toString()), -1, 0, 0);
@@ -691,11 +691,11 @@ public class Vector3DTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory3D<Vector3D> factory = Vector3D.getFactory();
+        RealFunction3N<Vector3D> factory = Vector3D.getFactory();
 
         // assert
-        checkVector(factory.create(1, 2, 3), 1, 2, 3);
-        checkVector(factory.create(-1, -2, -3), -1, -2, -3);
+        checkVector(factory.apply(1, 2, 3), 1, 2, 3);
+        checkVector(factory.apply(-1, -2, -3), -1, -2, -3);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
index b8c7238..e3d5127 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
@@ -1,5 +1,7 @@
 package org.apache.commons.geometry.euclidean.twod;
 
+import java.util.regex.Pattern;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -68,6 +70,20 @@ public class Cartesian2DTest {
         Assert.assertFalse(new StubCartesian2D(Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
     }
 
+    @Test
+    public void testToString() {
+        // arrange
+        StubCartesian2D c = new StubCartesian2D(1, 2);
+        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}\\)");
+
+        // act
+        String str = c.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
     private static class StubCartesian2D extends Cartesian2D {
         private static final long serialVersionUID = 1L;
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
index 20d95ad..d26f002 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
@@ -18,7 +18,7 @@ package org.apache.commons.geometry.euclidean.twod;
 
 import java.util.regex.Pattern;
 
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction2N;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -222,11 +222,11 @@ public class Point2DTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
+        RealFunction2N<Point2D> factory = Point2D.getFactory();
 
         // assert
-        checkPoint(factory.create(1, 2), 1, 2);
-        checkPoint(factory.create(-1, -2), -1, -2);
+        checkPoint(factory.apply(1, 2), 1, 2);
+        checkPoint(factory.apply(-1, -2), -1, -2);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
index 13208c0..fa130e8 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
@@ -3,7 +3,7 @@ package org.apache.commons.geometry.euclidean.twod;
 import java.util.regex.Pattern;
 
 import org.apache.commons.geometry.core.Geometry;
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction2N;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -421,7 +421,7 @@ public class Vector2DTest {
     public void testToString() {
         // arrange
         Vector2D v = Vector2D.of(1, 2);
-        Pattern pattern = Pattern.compile("\\{1.{0,2}, 2.{0,2}\\}");
+        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}\\)");
 
         // act
         String str = v.toString();
@@ -434,12 +434,12 @@ public class Vector2DTest {
     @Test
     public void testParse() {
         // act/assert
-        checkVector(Vector2D.parse("{1, 2}"), 1, 2);
-        checkVector(Vector2D.parse("{-1, -2}"), -1, -2);
+        checkVector(Vector2D.parse("(1, 2)"), 1, 2);
+        checkVector(Vector2D.parse("(-1, -2)"), -1, -2);
 
-        checkVector(Vector2D.parse("{0.01, -1e-3}"), 1e-2, -1e-3);
+        checkVector(Vector2D.parse("(0.01, -1e-3)"), 1e-2, -1e-3);
 
-        checkVector(Vector2D.parse("{NaN, -Infinity}"), Double.NaN, Double.NEGATIVE_INFINITY);
+        checkVector(Vector2D.parse("(NaN, -Infinity)"), Double.NaN, Double.NEGATIVE_INFINITY);
 
         checkVector(Vector2D.parse(Vector2D.ZERO.toString()), 0, 0);
         checkVector(Vector2D.parse(Vector2D.MINUS_X.toString()), -1, 0);
@@ -487,11 +487,11 @@ public class Vector2DTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory2D<Vector2D> factory = Vector2D.getFactory();
+        RealFunction2N<Vector2D> factory = Vector2D.getFactory();
 
         // assert
-        checkVector(factory.create(1, 2), 1, 2);
-        checkVector(factory.create(-1, -2), -1, -2);
+        checkVector(factory.apply(1, 2), 1, 2);
+        checkVector(factory.apply(-1, -2), -1, -2);
     }
 
     @Test
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
index 7cced20..9dd2d32 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
@@ -19,8 +19,8 @@ package org.apache.commons.geometry.spherical.oned;
 import java.io.Serializable;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.twod.Vector2D;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
@@ -38,12 +38,12 @@ public final class S1Point implements Point<S1Point>, Serializable {
     private static final long serialVersionUID = 20131218L;
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory1D<S1Point> FACTORY = new Coordinates.Factory1D<S1Point>() {
+    private static RealFunction<S1Point> FACTORY = new RealFunction<S1Point>() {
 
         /** {@inheritDoc} */
         @Override
-        public S1Point create(double a) {
-            return S1Point.of(a);
+        public S1Point apply(double n) {
+            return S1Point.of(n);
         }
     };
 
@@ -167,7 +167,7 @@ public final class S1Point implements Point<S1Point>, Serializable {
     /** {@inheritDoc} */
     @Override
     public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getAlpha());
+        return SimpleTupleFormat.getDefault().format(getAlpha());
     }
 
     /** Creates a new point instance from the given azimuthal coordinate value.
@@ -189,13 +189,13 @@ public final class S1Point implements Point<S1Point>, Serializable {
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static S1Point parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getPointFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new point instances.
      * @return point factory instance
      */
-    public static Coordinates.Factory1D<S1Point> getFactory() {
+    public static RealFunction<S1Point> getFactory() {
         return FACTORY;
     }
 }
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
index f8d3f4e..e5b2fbb 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
@@ -19,8 +19,8 @@ package org.apache.commons.geometry.spherical.twod;
 import java.io.Serializable;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.core.util.RealFunction2N;
+import org.apache.commons.geometry.core.util.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.euclidean.threed.Vector3D;
 
 /** This class represents a point on the 2-sphere.
@@ -62,12 +62,12 @@ public final class S2Point implements Point<S2Point>, Serializable {
     private static final long serialVersionUID = 20131218L;
 
     /** Factory for delegating instance creation. */
-    private static Coordinates.Factory2D<S2Point> FACTORY = new Coordinates.Factory2D<S2Point>() {
+    private static RealFunction2N<S2Point> FACTORY = new RealFunction2N<S2Point>() {
 
         /** {@inheritDoc} */
         @Override
-        public S2Point create(double a1, double a2) {
-            return S2Point.of(a1, a2);
+        public S2Point apply(double n1, double n2) {
+            return S2Point.of(n1, n2);
         }
     };
 
@@ -209,7 +209,7 @@ public final class S2Point implements Point<S2Point>, Serializable {
     /** {@inheritDoc} */
     @Override
     public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getTheta(), getPhi());
+        return SimpleTupleFormat.getDefault().format(getTheta(), getPhi());
     }
 
     /** Build a vector from its spherical coordinates
@@ -263,13 +263,13 @@ public final class S2Point implements Point<S2Point>, Serializable {
      * @throws IllegalArgumentException if the given string has an invalid format
      */
     public static S2Point parse(String str) throws IllegalArgumentException {
-        return SimpleCoordinateFormat.getPointFormat().parse(str, FACTORY);
+        return SimpleTupleFormat.getDefault().parse(str, FACTORY);
     }
 
     /** Returns a factory object that can be used to created new point instances.
      * @return point factory instance
      */
-    public static Coordinates.Factory2D<S2Point> getFactory() {
+    public static RealFunction2N<S2Point> getFactory() {
         return FACTORY;
     }
 }
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
index eac954d..e4f8b08 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
@@ -17,7 +17,7 @@
 package org.apache.commons.geometry.spherical.oned;
 
 import org.apache.commons.geometry.core.Geometry;
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -83,12 +83,12 @@ public class S1PointTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory1D<S1Point> factory = S1Point.getFactory();
+        RealFunction<S1Point> factory = S1Point.getFactory();
 
         // assert
-        checkPoint(factory.create(0), 0);
-        checkPoint(factory.create(1), 1);
-        checkPoint(factory.create(Geometry.TWO_PI), 0);
+        checkPoint(factory.apply(0), 0);
+        checkPoint(factory.apply(1), 1);
+        checkPoint(factory.apply(Geometry.TWO_PI), 0);
     }
 
     private void checkPoint(S1Point p, double alpha) {
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
index 1661921..1a04010 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
@@ -18,7 +18,7 @@ package org.apache.commons.geometry.spherical.twod;
 
 
 import org.apache.commons.geometry.core.Geometry;
-import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.RealFunction2N;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -100,11 +100,11 @@ public class S2PointTest {
     @Test
     public void testGetFactory() {
         // act
-        Coordinates.Factory2D<S2Point> factory = S2Point.getFactory();
+        RealFunction2N<S2Point> factory = S2Point.getFactory();
 
         // assert
-        checkPoint(factory.create(0, 0), 0, 0);
-        checkPoint(factory.create(1, 2), 1, 2);
+        checkPoint(factory.apply(0, 0), 0, 0);
+        checkPoint(factory.apply(1, 2), 1, 2);
     }
 
     private void checkPoint(S2Point p, double theta, double phi) {