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 2021/05/23 03:00:03 UTC

[commons-math] 01/04: MATH-1582: Modularization.

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

erans pushed a commit to branch modularized_master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 92094a0993015cbdc2be1c0d6553b31ca76f4fe5
Author: Gilles Sadowski <gi...@gmail.com>
AuthorDate: Sun May 23 01:15:57 2021 +0200

    MATH-1582: Modularization.
    
    Code moved from "o.a.c.math4.legacy.transform" into a dedicated module.
---
 .../math4/legacy/analysis/FunctionUtils.java       |  37 --
 .../legacy/transform/FastCosineTransformer.java    | 183 ---------
 .../legacy/transform/FastFourierTransformer.java   | 416 -------------------
 .../legacy/transform/FastSineTransformer.java      | 182 --------
 .../math4/legacy/transform/RealTransformer.java    |  67 ---
 .../math4/legacy/transform/TransformType.java      |  30 --
 .../math4/legacy/analysis/FunctionUtilsTest.java   |  27 --
 commons-math-transform/LICENCE                     | 457 +++++++++++++++++++++
 commons-math-transform/NOTICE                      |   9 +
 commons-math-transform/pom.xml                     |  82 ++++
 .../commons/math4/transform/ComplexTransform.java  |  66 +++
 .../commons/math4}/transform/DctNormalization.java |  12 +-
 .../commons/math4}/transform/DftNormalization.java |   4 +-
 .../commons/math4}/transform/DstNormalization.java |  12 +-
 .../math4/transform/FastCosineTransform.java       | 186 +++++++++
 .../math4/transform/FastFourierTransform.java      | 397 ++++++++++++++++++
 .../math4/transform/FastHadamardTransform.java     | 149 ++++---
 .../commons/math4/transform/FastSineTransform.java | 194 +++++++++
 .../commons/math4/transform/RealTransform.java     |  51 +++
 .../math4/transform/TransformException.java        |  52 +++
 .../commons/math4}/transform/TransformUtils.java   | 116 +++---
 .../commons/math4}/transform/package-info.java     |   7 +-
 .../transform/FastCosineTransformerTest.java       | 151 ++++---
 .../transform/FastFourierTransformerTest.java      | 325 +++++++--------
 .../transform/FastHadamardTransformerTest.java     |  42 +-
 .../math4}/transform/FastSineTransformerTest.java  | 160 ++++----
 .../transform/RealTransformerAbstractTest.java     | 184 ++++-----
 .../math4/transform/TransformUtilsTest.java        |  59 +++
 pom.xml                                            |   1 +
 src/changes/changes.xml                            |   3 +
 30 files changed, 2099 insertions(+), 1562 deletions(-)

diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/FunctionUtils.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/FunctionUtils.java
index 6f0e550..cd4ff48 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/FunctionUtils.java
+++ b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/FunctionUtils.java
@@ -302,43 +302,6 @@ public class FunctionUtils {
         };
     }
 
-    /**
-     * Samples the specified univariate real function on the specified interval.
-     * <p>
-     * The interval is divided equally into {@code n} sections and sample points
-     * are taken from {@code min} to {@code max - (max - min) / n}; therefore
-     * {@code f} is not sampled at the upper bound {@code max}.</p>
-     *
-     * @param f Function to be sampled
-     * @param min Lower bound of the interval (included).
-     * @param max Upper bound of the interval (excluded).
-     * @param n Number of sample points.
-     * @return the array of samples.
-     * @throws NumberIsTooLargeException if the lower bound {@code min} is
-     * greater than, or equal to the upper bound {@code max}.
-     * @throws NotStrictlyPositiveException if the number of sample points
-     * {@code n} is negative.
-     */
-    public static double[] sample(UnivariateFunction f, double min, double max, int n)
-       throws NumberIsTooLargeException, NotStrictlyPositiveException {
-
-        if (n <= 0) {
-            throw new NotStrictlyPositiveException(
-                    LocalizedFormats.NOT_POSITIVE_NUMBER_OF_SAMPLES,
-                    Integer.valueOf(n));
-        }
-        if (min >= max) {
-            throw new NumberIsTooLargeException(min, max, false);
-        }
-
-        final double[] s = new double[n];
-        final double h = (max - min) / n;
-        for (int i = 0; i < n; i++) {
-            s[i] = f.value(min + i * h);
-        }
-        return s;
-    }
-
     /** Convert regular functions to {@link UnivariateDifferentiableFunction}.
      * <p>
      * This method handle the case with one free parameter and several derivatives.
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastCosineTransformer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastCosineTransformer.java
deleted file mode 100644
index e95d5e4..0000000
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastCosineTransformer.java
+++ /dev/null
@@ -1,183 +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.math4.legacy.transform;
-
-import java.io.Serializable;
-
-import org.apache.commons.numbers.complex.Complex;
-import org.apache.commons.numbers.core.ArithmeticUtils;
-import org.apache.commons.math4.legacy.analysis.FunctionUtils;
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
-import org.apache.commons.math4.legacy.util.FastMath;
-
-/**
- * Implements the Fast Cosine Transform for transformation of one-dimensional
- * real data sets. For reference, see James S. Walker, <em>Fast Fourier
- * Transforms</em>, chapter 3 (ISBN 0849371635).
- * <p>
- * There are several variants of the discrete cosine transform. The present
- * implementation corresponds to DCT-I, with various normalization conventions,
- * which are specified by the parameter {@link DctNormalization}.
- * <p>
- * DCT-I is equivalent to DFT of an <em>even extension</em> of the data series.
- * More precisely, if x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is the data set
- * to be cosine transformed, the extended data set
- * x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-3</sub><sup>&#35;</sup>
- * is defined as follows
- * <ul>
- * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>k</sub> if 0 &le; k &lt; N,</li>
- * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>2N-2-k</sub>
- * if N &le; k &lt; 2N - 2.</li>
- * </ul>
- * <p>
- * Then, the standard DCT-I y<sub>0</sub>, &hellip;, y<sub>N-1</sub> of the real
- * data set x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is equal to <em>half</em>
- * of the N first elements of the DFT of the extended data set
- * x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-3</sub><sup>&#35;</sup>
- * <br>
- * y<sub>n</sub> = (1 / 2) &sum;<sub>k=0</sub><sup>2N-3</sup>
- * x<sub>k</sub><sup>&#35;</sup> exp[-2&pi;i nk / (2N - 2)]
- * &nbsp;&nbsp;&nbsp;&nbsp;k = 0, &hellip;, N-1.
- * <p>
- * The present implementation of the discrete cosine transform as a fast cosine
- * transform requires the length of the data set to be a power of two plus one
- * (N&nbsp;=&nbsp;2<sup>n</sup>&nbsp;+&nbsp;1). Besides, it implicitly assumes
- * that the sampled function is even.
- *
- * @since 1.2
- */
-public class FastCosineTransformer implements RealTransformer, Serializable {
-
-    /** Serializable version identifier. */
-    static final long serialVersionUID = 20120212L;
-
-    /** The type of DCT to be performed. */
-    private final DctNormalization normalization;
-
-    /**
-     * Creates a new instance of this class, with various normalization
-     * conventions.
-     *
-     * @param normalization the type of normalization to be applied to the
-     * transformed data
-     */
-    public FastCosineTransformer(final DctNormalization normalization) {
-        this.normalization = normalization;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @throws MathIllegalArgumentException if the length of the data array is
-     * not a power of two plus one
-     */
-    @Override
-    public double[] transform(final double[] f, final TransformType type)
-      throws MathIllegalArgumentException {
-        if (type == TransformType.FORWARD) {
-            if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
-                final double s = FastMath.sqrt(2.0 / (f.length - 1));
-                return TransformUtils.scaleArray(fct(f), s);
-            }
-            return fct(f);
-        }
-        final double s2 = 2.0 / (f.length - 1);
-        final double s1;
-        if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
-            s1 = FastMath.sqrt(s2);
-        } else {
-            s1 = s2;
-        }
-        return TransformUtils.scaleArray(fct(f), s1);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @throws org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException
-     * if the lower bound is greater than, or equal to the upper bound
-     * @throws org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException
-     * if the number of sample points is negative
-     * @throws MathIllegalArgumentException if the number of sample points is
-     * not a power of two plus one
-     */
-    @Override
-    public double[] transform(final UnivariateFunction f,
-        final double min, final double max, final int n,
-        final TransformType type) throws MathIllegalArgumentException {
-
-        final double[] data = FunctionUtils.sample(f, min, max, n);
-        return transform(data, type);
-    }
-
-    /**
-     * Perform the FCT algorithm (including inverse).
-     *
-     * @param f the real data array to be transformed
-     * @return the real transformed array
-     * @throws MathIllegalArgumentException if the length of the data array is
-     * not a power of two plus one
-     */
-    protected double[] fct(double[] f)
-        throws MathIllegalArgumentException {
-
-        final double[] transformed = new double[f.length];
-
-        final int n = f.length - 1;
-        if (!ArithmeticUtils.isPowerOfTwo(n)) {
-            throw new MathIllegalArgumentException(
-                LocalizedFormats.NOT_POWER_OF_TWO_PLUS_ONE,
-                Integer.valueOf(f.length));
-        }
-        if (n == 1) {       // trivial case
-            transformed[0] = 0.5 * (f[0] + f[1]);
-            transformed[1] = 0.5 * (f[0] - f[1]);
-            return transformed;
-        }
-
-        // construct a new array and perform FFT on it
-        final double[] x = new double[n];
-        x[0] = 0.5 * (f[0] + f[n]);
-        x[n >> 1] = f[n >> 1];
-        // temporary variable for transformed[1]
-        double t1 = 0.5 * (f[0] - f[n]);
-        for (int i = 1; i < (n >> 1); i++) {
-            final double a = 0.5 * (f[i] + f[n - i]);
-            final double b = FastMath.sin(i * FastMath.PI / n) * (f[i] - f[n - i]);
-            final double c = FastMath.cos(i * FastMath.PI / n) * (f[i] - f[n - i]);
-            x[i] = a - b;
-            x[n - i] = a + b;
-            t1 += c;
-        }
-        FastFourierTransformer transformer;
-        transformer = new FastFourierTransformer(DftNormalization.STANDARD);
-        Complex[] y = transformer.transform(x, TransformType.FORWARD);
-
-        // reconstruct the FCT result for the original array
-        transformed[0] = y[0].getReal();
-        transformed[1] = t1;
-        for (int i = 1; i < (n >> 1); i++) {
-            transformed[2 * i]     = y[i].getReal();
-            transformed[2 * i + 1] = transformed[2 * i - 1] - y[i].getImaginary();
-        }
-        transformed[n] = y[n >> 1].getReal();
-
-        return transformed;
-    }
-}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastFourierTransformer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastFourierTransformer.java
deleted file mode 100644
index 23d10c4..0000000
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastFourierTransformer.java
+++ /dev/null
@@ -1,416 +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.math4.legacy.transform;
-
-import java.io.Serializable;
-import java.util.Arrays;
-import org.apache.commons.numbers.complex.Complex;
-import org.apache.commons.numbers.core.ArithmeticUtils;
-import org.apache.commons.math4.legacy.analysis.FunctionUtils;
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
-import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
-import org.apache.commons.math4.legacy.util.FastMath;
-
-/**
- * Implements the Fast Fourier Transform for transformation of one-dimensional
- * real or complex data sets. For reference, see <em>Applied Numerical Linear
- * Algebra</em>, ISBN 0898713897, chapter 6.
- * <p>
- * There are several variants of the discrete Fourier transform, with various
- * normalization conventions, which are specified by the parameter
- * {@link DftNormalization}.
- * <p>
- * The current implementation of the discrete Fourier transform as a fast
- * Fourier transform requires the length of the data set to be a power of 2.
- * This greatly simplifies and speeds up the code. Users can pad the data with
- * zeros to meet this requirement. There are other flavors of FFT, for
- * reference, see S. Winograd,
- * <i>On computing the discrete Fourier transform</i>, Mathematics of
- * Computation, 32 (1978), 175 - 199.
- *
- * @see DftNormalization
- * @since 1.2
- */
-public class FastFourierTransformer implements Serializable {
-    /** Serializable version identifier. */
-    static final long serialVersionUID = 20120210L;
-
-    /**
-     * {@code W_SUB_N_R[i]} is the real part of
-     * {@code exp(- 2 * i * pi / n)}:
-     * {@code W_SUB_N_R[i] = cos(2 * pi/ n)}, where {@code n = 2^i}.
-     */
-    private static final double[] W_SUB_N_R =
-            {  0x1.0p0, -0x1.0p0, 0x1.1a62633145c07p-54, 0x1.6a09e667f3bcdp-1
-            , 0x1.d906bcf328d46p-1, 0x1.f6297cff75cbp-1, 0x1.fd88da3d12526p-1, 0x1.ff621e3796d7ep-1
-            , 0x1.ffd886084cd0dp-1, 0x1.fff62169b92dbp-1, 0x1.fffd8858e8a92p-1, 0x1.ffff621621d02p-1
-            , 0x1.ffffd88586ee6p-1, 0x1.fffff62161a34p-1, 0x1.fffffd8858675p-1, 0x1.ffffff621619cp-1
-            , 0x1.ffffffd885867p-1, 0x1.fffffff62161ap-1, 0x1.fffffffd88586p-1, 0x1.ffffffff62162p-1
-            , 0x1.ffffffffd8858p-1, 0x1.fffffffff6216p-1, 0x1.fffffffffd886p-1, 0x1.ffffffffff621p-1
-            , 0x1.ffffffffffd88p-1, 0x1.fffffffffff62p-1, 0x1.fffffffffffd9p-1, 0x1.ffffffffffff6p-1
-            , 0x1.ffffffffffffep-1, 0x1.fffffffffffffp-1, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
-            , 0x1.0p0, 0x1.0p0, 0x1.0p0 };
-
-    /**
-     * {@code W_SUB_N_I[i]} is the imaginary part of
-     * {@code exp(- 2 * i * pi / n)}:
-     * {@code W_SUB_N_I[i] = -sin(2 * pi/ n)}, where {@code n = 2^i}.
-     */
-    private static final double[] W_SUB_N_I =
-            {  0x1.1a62633145c07p-52, -0x1.1a62633145c07p-53, -0x1.0p0, -0x1.6a09e667f3bccp-1
-            , -0x1.87de2a6aea963p-2, -0x1.8f8b83c69a60ap-3, -0x1.917a6bc29b42cp-4, -0x1.91f65f10dd814p-5
-            , -0x1.92155f7a3667ep-6, -0x1.921d1fcdec784p-7, -0x1.921f0fe670071p-8, -0x1.921f8becca4bap-9
-            , -0x1.921faaee6472dp-10, -0x1.921fb2aecb36p-11, -0x1.921fb49ee4ea6p-12, -0x1.921fb51aeb57bp-13
-            , -0x1.921fb539ecf31p-14, -0x1.921fb541ad59ep-15, -0x1.921fb5439d73ap-16, -0x1.921fb544197ap-17
-            , -0x1.921fb544387bap-18, -0x1.921fb544403c1p-19, -0x1.921fb544422c2p-20, -0x1.921fb54442a83p-21
-            , -0x1.921fb54442c73p-22, -0x1.921fb54442cefp-23, -0x1.921fb54442d0ep-24, -0x1.921fb54442d15p-25
-            , -0x1.921fb54442d17p-26, -0x1.921fb54442d18p-27, -0x1.921fb54442d18p-28, -0x1.921fb54442d18p-29
-            , -0x1.921fb54442d18p-30, -0x1.921fb54442d18p-31, -0x1.921fb54442d18p-32, -0x1.921fb54442d18p-33
-            , -0x1.921fb54442d18p-34, -0x1.921fb54442d18p-35, -0x1.921fb54442d18p-36, -0x1.921fb54442d18p-37
-            , -0x1.921fb54442d18p-38, -0x1.921fb54442d18p-39, -0x1.921fb54442d18p-40, -0x1.921fb54442d18p-41
-            , -0x1.921fb54442d18p-42, -0x1.921fb54442d18p-43, -0x1.921fb54442d18p-44, -0x1.921fb54442d18p-45
-            , -0x1.921fb54442d18p-46, -0x1.921fb54442d18p-47, -0x1.921fb54442d18p-48, -0x1.921fb54442d18p-49
-            , -0x1.921fb54442d18p-50, -0x1.921fb54442d18p-51, -0x1.921fb54442d18p-52, -0x1.921fb54442d18p-53
-            , -0x1.921fb54442d18p-54, -0x1.921fb54442d18p-55, -0x1.921fb54442d18p-56, -0x1.921fb54442d18p-57
-            , -0x1.921fb54442d18p-58, -0x1.921fb54442d18p-59, -0x1.921fb54442d18p-60 };
-
-    /** The type of DFT to be performed. */
-    private final DftNormalization normalization;
-
-    /**
-     * Creates a new instance of this class, with various normalization
-     * conventions.
-     *
-     * @param normalization the type of normalization to be applied to the
-     * transformed data
-     */
-    public FastFourierTransformer(final DftNormalization normalization) {
-        this.normalization = normalization;
-    }
-
-    /**
-     * Performs identical index bit reversal shuffles on two arrays of identical
-     * size. Each element in the array is swapped with another element based on
-     * the bit-reversal of the index. For example, in an array with length 16,
-     * item at binary index 0011 (decimal 3) would be swapped with the item at
-     * binary index 1100 (decimal 12).
-     *
-     * @param a the first array to be shuffled
-     * @param b the second array to be shuffled
-     */
-    private static void bitReversalShuffle2(double[] a, double[] b) {
-        final int n = a.length;
-        assert b.length == n;
-        final int halfOfN = n >> 1;
-
-        int j = 0;
-        for (int i = 0; i < n; i++) {
-            if (i < j) {
-                // swap indices i & j
-                double temp = a[i];
-                a[i] = a[j];
-                a[j] = temp;
-
-                temp = b[i];
-                b[i] = b[j];
-                b[j] = temp;
-            }
-
-            int k = halfOfN;
-            while (k <= j && k > 0) {
-                j -= k;
-                k >>= 1;
-            }
-            j += k;
-        }
-    }
-
-    /**
-     * Applies the proper normalization to the specified transformed data.
-     *
-     * @param dataRI the unscaled transformed data
-     * @param normalization the normalization to be applied
-     * @param type the type of transform (forward, inverse) which resulted in the specified data
-     */
-    private static void normalizeTransformedData(final double[][] dataRI,
-        final DftNormalization normalization, final TransformType type) {
-
-        final double[] dataR = dataRI[0];
-        final double[] dataI = dataRI[1];
-        final int n = dataR.length;
-        assert dataI.length == n;
-
-        switch (normalization) {
-            case STANDARD:
-                if (type == TransformType.INVERSE) {
-                    final double scaleFactor = 1.0 / ((double) n);
-                    for (int i = 0; i < n; i++) {
-                        dataR[i] *= scaleFactor;
-                        dataI[i] *= scaleFactor;
-                    }
-                }
-                break;
-            case UNITARY:
-                final double scaleFactor = 1.0 / FastMath.sqrt(n);
-                for (int i = 0; i < n; i++) {
-                    dataR[i] *= scaleFactor;
-                    dataI[i] *= scaleFactor;
-                }
-                break;
-            default:
-                /*
-                 * This should never occur in normal conditions. However this
-                 * clause has been added as a safeguard if other types of
-                 * normalizations are ever implemented, and the corresponding
-                 * test is forgotten in the present switch.
-                 */
-                throw new MathIllegalStateException();
-        }
-    }
-
-    /**
-     * Computes the standard transform of the specified complex data. The
-     * computation is done in place. The input data is laid out as follows
-     * <ul>
-     *   <li>{@code dataRI[0][i]} is the real part of the {@code i}-th data point,</li>
-     *   <li>{@code dataRI[1][i]} is the imaginary part of the {@code i}-th data point.</li>
-     * </ul>
-     *
-     * @param dataRI the two dimensional array of real and imaginary parts of the data
-     * @param normalization the normalization to be applied to the transformed data
-     * @param type the type of transform (forward, inverse) to be performed
-     * @throws DimensionMismatchException if the number of rows of the specified
-     *   array is not two, or the array is not rectangular
-     * @throws MathIllegalArgumentException if the number of data points is not
-     *   a power of two
-     */
-    public static void transformInPlace(final double[][] dataRI,
-        final DftNormalization normalization, final TransformType type) {
-
-        if (dataRI.length != 2) {
-            throw new DimensionMismatchException(dataRI.length, 2);
-        }
-        final double[] dataR = dataRI[0];
-        final double[] dataI = dataRI[1];
-        if (dataR.length != dataI.length) {
-            throw new DimensionMismatchException(dataI.length, dataR.length);
-        }
-
-        final int n = dataR.length;
-        if (!ArithmeticUtils.isPowerOfTwo(n)) {
-            throw new MathIllegalArgumentException(
-                LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING,
-                Integer.valueOf(n));
-        }
-
-        if (n == 1) {
-            return;
-        } else if (n == 2) {
-            final double srcR0 = dataR[0];
-            final double srcI0 = dataI[0];
-            final double srcR1 = dataR[1];
-            final double srcI1 = dataI[1];
-
-            // X_0 = x_0 + x_1
-            dataR[0] = srcR0 + srcR1;
-            dataI[0] = srcI0 + srcI1;
-            // X_1 = x_0 - x_1
-            dataR[1] = srcR0 - srcR1;
-            dataI[1] = srcI0 - srcI1;
-
-            normalizeTransformedData(dataRI, normalization, type);
-            return;
-        }
-
-        bitReversalShuffle2(dataR, dataI);
-
-        // Do 4-term DFT.
-        if (type == TransformType.INVERSE) {
-            for (int i0 = 0; i0 < n; i0 += 4) {
-                final int i1 = i0 + 1;
-                final int i2 = i0 + 2;
-                final int i3 = i0 + 3;
-
-                final double srcR0 = dataR[i0];
-                final double srcI0 = dataI[i0];
-                final double srcR1 = dataR[i2];
-                final double srcI1 = dataI[i2];
-                final double srcR2 = dataR[i1];
-                final double srcI2 = dataI[i1];
-                final double srcR3 = dataR[i3];
-                final double srcI3 = dataI[i3];
-
-                // 4-term DFT
-                // X_0 = x_0 + x_1 + x_2 + x_3
-                dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
-                dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
-                // X_1 = x_0 - x_2 + j * (x_3 - x_1)
-                dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1);
-                dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3);
-                // X_2 = x_0 - x_1 + x_2 - x_3
-                dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
-                dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
-                // X_3 = x_0 - x_2 + j * (x_1 - x_3)
-                dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3);
-                dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1);
-            }
-        } else {
-            for (int i0 = 0; i0 < n; i0 += 4) {
-                final int i1 = i0 + 1;
-                final int i2 = i0 + 2;
-                final int i3 = i0 + 3;
-
-                final double srcR0 = dataR[i0];
-                final double srcI0 = dataI[i0];
-                final double srcR1 = dataR[i2];
-                final double srcI1 = dataI[i2];
-                final double srcR2 = dataR[i1];
-                final double srcI2 = dataI[i1];
-                final double srcR3 = dataR[i3];
-                final double srcI3 = dataI[i3];
-
-                // 4-term DFT
-                // X_0 = x_0 + x_1 + x_2 + x_3
-                dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
-                dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
-                // X_1 = x_0 - x_2 + j * (x_3 - x_1)
-                dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3);
-                dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1);
-                // X_2 = x_0 - x_1 + x_2 - x_3
-                dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
-                dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
-                // X_3 = x_0 - x_2 + j * (x_1 - x_3)
-                dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1);
-                dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3);
-            }
-        }
-
-        int lastN0 = 4;
-        int lastLogN0 = 2;
-        while (lastN0 < n) {
-            int n0 = lastN0 << 1;
-            int logN0 = lastLogN0 + 1;
-            double wSubN0R = W_SUB_N_R[logN0];
-            double wSubN0I = W_SUB_N_I[logN0];
-            if (type == TransformType.INVERSE) {
-                wSubN0I = -wSubN0I;
-            }
-
-            // Combine even/odd transforms of size lastN0 into a transform of size N0 (lastN0 * 2).
-            for (int destEvenStartIndex = 0; destEvenStartIndex < n; destEvenStartIndex += n0) {
-                int destOddStartIndex = destEvenStartIndex + lastN0;
-
-                double wSubN0ToRR = 1;
-                double wSubN0ToRI = 0;
-
-                for (int r = 0; r < lastN0; r++) {
-                    double grR = dataR[destEvenStartIndex + r];
-                    double grI = dataI[destEvenStartIndex + r];
-                    double hrR = dataR[destOddStartIndex + r];
-                    double hrI = dataI[destOddStartIndex + r];
-
-                    // dest[destEvenStartIndex + r] = Gr + WsubN0ToR * Hr
-                    dataR[destEvenStartIndex + r] = grR + wSubN0ToRR * hrR - wSubN0ToRI * hrI;
-                    dataI[destEvenStartIndex + r] = grI + wSubN0ToRR * hrI + wSubN0ToRI * hrR;
-                    // dest[destOddStartIndex + r] = Gr - WsubN0ToR * Hr
-                    dataR[destOddStartIndex + r] = grR - (wSubN0ToRR * hrR - wSubN0ToRI * hrI);
-                    dataI[destOddStartIndex + r] = grI - (wSubN0ToRR * hrI + wSubN0ToRI * hrR);
-
-                    // WsubN0ToR *= WsubN0R
-                    double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I;
-                    double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R;
-                    wSubN0ToRR = nextWsubN0ToRR;
-                    wSubN0ToRI = nextWsubN0ToRI;
-                }
-            }
-
-            lastN0 = n0;
-            lastLogN0 = logN0;
-        }
-
-        normalizeTransformedData(dataRI, normalization, type);
-    }
-
-    /**
-     * Returns the (forward, inverse) transform of the specified real data set.
-     *
-     * @param f the real data array to be transformed
-     * @param type the type of transform (forward, inverse) to be performed
-     * @return the complex transformed array
-     * @throws MathIllegalArgumentException if the length of the data array is not a power of two
-     */
-    public Complex[] transform(final double[] f, final TransformType type) {
-        final double[][] dataRI = new double[][] {
-            Arrays.copyOf(f, f.length), new double[f.length]
-        };
-
-        transformInPlace(dataRI, normalization, type);
-
-        return TransformUtils.createComplexArray(dataRI);
-    }
-
-    /**
-     * Returns the (forward, inverse) transform of the specified real function,
-     * sampled on the specified interval.
-     *
-     * @param f the function to be sampled and transformed
-     * @param min the (inclusive) lower bound for the interval
-     * @param max the (exclusive) upper bound for the interval
-     * @param n the number of sample points
-     * @param type the type of transform (forward, inverse) to be performed
-     * @return the complex transformed array
-     * @throws org.apache.commons.math4.legacy.exception.NumberIsTooLargeException
-     *   if the lower bound is greater than, or equal to the upper bound
-     * @throws org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException
-     *   if the number of sample points {@code n} is negative
-     * @throws MathIllegalArgumentException if the number of sample points
-     *   {@code n} is not a power of two
-     */
-    public Complex[] transform(final UnivariateFunction f,
-                               final double min, final double max, final int n,
-                               final TransformType type) {
-
-        final double[] data = FunctionUtils.sample(f, min, max, n);
-        return transform(data, type);
-    }
-
-    /**
-     * Returns the (forward, inverse) transform of the specified complex data set.
-     *
-     * @param f the complex data array to be transformed
-     * @param type the type of transform (forward, inverse) to be performed
-     * @return the complex transformed array
-     * @throws MathIllegalArgumentException if the length of the data array is not a power of two
-     */
-    public Complex[] transform(final Complex[] f, final TransformType type) {
-        final double[][] dataRI = TransformUtils.createRealImaginaryArray(f);
-
-        transformInPlace(dataRI, normalization, type);
-
-        return TransformUtils.createComplexArray(dataRI);
-    }
-}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastSineTransformer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastSineTransformer.java
deleted file mode 100644
index 09707a0..0000000
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastSineTransformer.java
+++ /dev/null
@@ -1,182 +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.math4.legacy.transform;
-
-import java.io.Serializable;
-
-import org.apache.commons.numbers.complex.Complex;
-import org.apache.commons.numbers.core.ArithmeticUtils;
-import org.apache.commons.math4.legacy.analysis.FunctionUtils;
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
-import org.apache.commons.math4.legacy.util.FastMath;
-
-/**
- * Implements the Fast Sine Transform for transformation of one-dimensional real
- * data sets. For reference, see James S. Walker, <em>Fast Fourier
- * Transforms</em>, chapter 3 (ISBN 0849371635).
- * <p>
- * There are several variants of the discrete sine transform. The present
- * implementation corresponds to DST-I, with various normalization conventions,
- * which are specified by the parameter {@link DstNormalization}.
- * <strong>It should be noted that regardless to the convention, the first
- * element of the dataset to be transformed must be zero.</strong>
- * <p>
- * DST-I is equivalent to DFT of an <em>odd extension</em> of the data series.
- * More precisely, if x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is the data set
- * to be sine transformed, the extended data set x<sub>0</sub><sup>&#35;</sup>,
- * &hellip;, x<sub>2N-1</sub><sup>&#35;</sup> is defined as follows
- * <ul>
- * <li>x<sub>0</sub><sup>&#35;</sup> = x<sub>0</sub> = 0,</li>
- * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>k</sub> if 1 &le; k &lt; N,</li>
- * <li>x<sub>N</sub><sup>&#35;</sup> = 0,</li>
- * <li>x<sub>k</sub><sup>&#35;</sup> = -x<sub>2N-k</sub> if N + 1 &le; k &lt;
- * 2N.</li>
- * </ul>
- * <p>
- * Then, the standard DST-I y<sub>0</sub>, &hellip;, y<sub>N-1</sub> of the real
- * data set x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is equal to <em>half</em>
- * of i (the pure imaginary number) times the N first elements of the DFT of the
- * extended data set x<sub>0</sub><sup>&#35;</sup>, &hellip;,
- * x<sub>2N-1</sub><sup>&#35;</sup> <br>
- * y<sub>n</sub> = (i / 2) &sum;<sub>k=0</sub><sup>2N-1</sup>
- * x<sub>k</sub><sup>&#35;</sup> exp[-2&pi;i nk / (2N)]
- * &nbsp;&nbsp;&nbsp;&nbsp;k = 0, &hellip;, N-1.
- * <p>
- * The present implementation of the discrete sine transform as a fast sine
- * transform requires the length of the data to be a power of two. Besides,
- * it implicitly assumes that the sampled function is odd. In particular, the
- * first element of the data set must be 0, which is enforced in
- * {@link #transform(UnivariateFunction, double, double, int, TransformType)},
- * after sampling.
- *
- * @since 1.2
- */
-public class FastSineTransformer implements RealTransformer, Serializable {
-
-    /** Serializable version identifier. */
-    static final long serialVersionUID = 20120211L;
-
-    /** The type of DST to be performed. */
-    private final DstNormalization normalization;
-
-    /**
-     * Creates a new instance of this class, with various normalization conventions.
-     *
-     * @param normalization the type of normalization to be applied to the transformed data
-     */
-    public FastSineTransformer(final DstNormalization normalization) {
-        this.normalization = normalization;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * The first element of the specified data set is required to be {@code 0}.
-     *
-     * @throws MathIllegalArgumentException if the length of the data array is
-     *   not a power of two, or the first element of the data array is not zero
-     */
-    @Override
-    public double[] transform(final double[] f, final TransformType type) {
-        if (normalization == DstNormalization.ORTHOGONAL_DST_I) {
-            final double s = FastMath.sqrt(2.0 / f.length);
-            return TransformUtils.scaleArray(fst(f), s);
-        }
-        if (type == TransformType.FORWARD) {
-            return fst(f);
-        }
-        final double s = 2.0 / f.length;
-        return TransformUtils.scaleArray(fst(f), s);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * This implementation enforces {@code f(x) = 0.0} at {@code x = 0.0}.
-     *
-     * @throws org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException
-     *   if the lower bound is greater than, or equal to the upper bound
-     * @throws org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException
-     *   if the number of sample points is negative
-     * @throws MathIllegalArgumentException if the number of sample points is not a power of two
-     */
-    @Override
-    public double[] transform(final UnivariateFunction f,
-        final double min, final double max, final int n,
-        final TransformType type) {
-
-        final double[] data = FunctionUtils.sample(f, min, max, n);
-        data[0] = 0.0;
-        return transform(data, type);
-    }
-
-    /**
-     * Perform the FST algorithm (including inverse). The first element of the
-     * data set is required to be {@code 0}.
-     *
-     * @param f the real data array to be transformed
-     * @return the real transformed array
-     * @throws MathIllegalArgumentException if the length of the data array is
-     *   not a power of two, or the first element of the data array is not zero
-     */
-    protected double[] fst(double[] f) throws MathIllegalArgumentException {
-
-        final double[] transformed = new double[f.length];
-
-        if (!ArithmeticUtils.isPowerOfTwo(f.length)) {
-            throw new MathIllegalArgumentException(
-                    LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING,
-                    Integer.valueOf(f.length));
-        }
-        if (f[0] != 0.0) {
-            throw new MathIllegalArgumentException(
-                    LocalizedFormats.FIRST_ELEMENT_NOT_ZERO,
-                    Double.valueOf(f[0]));
-        }
-        final int n = f.length;
-        if (n == 1) {       // trivial case
-            transformed[0] = 0.0;
-            return transformed;
-        }
-
-        // construct a new array and perform FFT on it
-        final double[] x = new double[n];
-        x[0] = 0.0;
-        x[n >> 1] = 2.0 * f[n >> 1];
-        for (int i = 1; i < (n >> 1); i++) {
-            final double a = FastMath.sin(i * FastMath.PI / n) * (f[i] + f[n - i]);
-            final double b = 0.5 * (f[i] - f[n - i]);
-            x[i]     = a + b;
-            x[n - i] = a - b;
-        }
-        FastFourierTransformer transformer;
-        transformer = new FastFourierTransformer(DftNormalization.STANDARD);
-        Complex[] y = transformer.transform(x, TransformType.FORWARD);
-
-        // reconstruct the FST result for the original array
-        transformed[0] = 0.0;
-        transformed[1] = 0.5 * y[0].getReal();
-        for (int i = 1; i < (n >> 1); i++) {
-            transformed[2 * i]     = -y[i].getImaginary();
-            transformed[2 * i + 1] = y[i].getReal() + transformed[2 * i - 1];
-        }
-
-        return transformed;
-    }
-}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/RealTransformer.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/RealTransformer.java
deleted file mode 100644
index 3009c77..0000000
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/RealTransformer.java
+++ /dev/null
@@ -1,67 +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.math4.legacy.transform;
-
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException;
-import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
-
-/**
- * Interface for one-dimensional data sets transformations producing real results.
- * <p>
- * Such transforms include {@link FastSineTransformer sine transform},
- * {@link FastCosineTransformer cosine transform} or {@link
- * FastHadamardTransformer Hadamard transform}.
- *
- * @since 2.0
- */
-public interface RealTransformer  {
-
-    /**
-     * Returns the (forward, inverse) transform of the specified real data set.
-     *
-     * @param f the real data array to be transformed (signal)
-     * @param type the type of transform (forward, inverse) to be performed
-     * @return the real transformed array (spectrum)
-     * @throws MathIllegalArgumentException if the array cannot be transformed
-     *   with the given type (this may be for example due to array size, which is
-     *   constrained in some transforms)
-     */
-    double[] transform(double[] f, TransformType type) throws MathIllegalArgumentException;
-
-    /**
-     * Returns the (forward, inverse) transform of the specified real function,
-     * sampled on the specified interval.
-     *
-     * @param f the function to be sampled and transformed
-     * @param min the (inclusive) lower bound for the interval
-     * @param max the (exclusive) upper bound for the interval
-     * @param n the number of sample points
-     * @param type the type of transform (forward, inverse) to be performed
-     * @return the real transformed array
-     * @throws NonMonotonicSequenceException if the lower bound is greater than, or equal to the upper bound
-     * @throws NotStrictlyPositiveException if the number of sample points is negative
-     * @throws MathIllegalArgumentException if the sample cannot be transformed
-     *   with the given type (this may be for example due to sample size, which is
-     *   constrained in some transforms)
-     */
-    double[] transform(UnivariateFunction f, double min, double max, int n,
-                       TransformType type)
-        throws NonMonotonicSequenceException, NotStrictlyPositiveException, MathIllegalArgumentException;
-
-}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/TransformType.java b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/TransformType.java
deleted file mode 100644
index 9207cbb..0000000
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/TransformType.java
+++ /dev/null
@@ -1,30 +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.math4.legacy.transform;
-
-/**
- * This enumeration defines the type of transform which is to be computed.
- *
- * @since 3.0
- */
-public enum TransformType {
-    /** The type to be specified for forward transforms. */
-    FORWARD,
-
-    /** The type to be specified for inverse transforms. */
-    INVERSE;
-}
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/FunctionUtilsTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/FunctionUtilsTest.java
index af69078..01ffeb0 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/FunctionUtilsTest.java
+++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/FunctionUtilsTest.java
@@ -208,33 +208,6 @@ public class FunctionUtilsTest {
         }
     }
 
-    @Test(expected = NumberIsTooLargeException.class)
-    public void testSampleWrongBounds(){
-        FunctionUtils.sample(new Sin(), FastMath.PI, 0.0, 10);
-    }
-
-    @Test(expected = NotStrictlyPositiveException.class)
-    public void testSampleNegativeNumberOfPoints(){
-        FunctionUtils.sample(new Sin(), 0.0, FastMath.PI, -1);
-    }
-
-    @Test(expected = NotStrictlyPositiveException.class)
-    public void testSampleNullNumberOfPoints(){
-        FunctionUtils.sample(new Sin(), 0.0, FastMath.PI, 0);
-    }
-
-    @Test
-    public void testSample() {
-        final int n = 11;
-        final double min = 0.0;
-        final double max = FastMath.PI;
-        final double[] actual = FunctionUtils.sample(new Sin(), min, max, n);
-        for (int i = 0; i < n; i++) {
-            final double x = min + (max - min) / n * i;
-            Assert.assertEquals("x = " + x, FastMath.sin(x), actual[i], 0.0);
-        }
-    }
-
     @Test
     public void testToDifferentiableUnivariate() {
 
diff --git a/commons-math-transform/LICENCE b/commons-math-transform/LICENCE
new file mode 100644
index 0000000..d97b49a
--- /dev/null
+++ b/commons-math-transform/LICENCE
@@ -0,0 +1,457 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+Apache Commons Math includes the following code provided to the ASF under the
+Apache License 2.0:
+
+ - The inverse error function implementation in the Erf class is based on CUDA
+   code developed by Mike Giles, Oxford-Man Institute of Quantitative Finance,
+   and published in GPU Computing Gems, volume 2, 2010 (grant received on
+   March 23th 2013)
+ - The LinearConstraint, LinearObjectiveFunction, LinearOptimizer,
+   RelationShip, SimplexSolver and SimplexTableau classes in package
+   org.apache.commons.math3.optimization.linear include software developed by
+   Benjamin McCann (http://www.benmccann.com) and distributed with
+   the following copyright: Copyright 2009 Google Inc. (grant received on
+   March 16th 2009)
+ - The class "org.apache.commons.math3.exception.util.LocalizedFormatsTest" which
+   is an adapted version of "OrekitMessagesTest" test class for the Orekit library
+ - The "org.apache.commons.math3.analysis.interpolation.HermiteInterpolator"
+   has been imported from the Orekit space flight dynamics library.
+
+===============================================================================
+ 
+
+
+APACHE COMMONS MATH DERIVATIVE WORKS: 
+
+The Apache commons-math library includes a number of subcomponents
+whose implementation is derived from original sources written
+in C or Fortran.  License terms of the original sources
+are reproduced below.
+
+===============================================================================
+For the lmder, lmpar and qrsolv Fortran routine from minpack and translated in
+the LevenbergMarquardtOptimizer class in package
+org.apache.commons.math3.optimization.general 
+Original source copyright and license statement:
+
+Minpack Copyright Notice (1999) University of Chicago.  All rights reserved
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above
+copyright notice, this list of conditions and the following
+disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials
+provided with the distribution.
+
+3. The end-user documentation included with the
+redistribution, if any, must include the following
+acknowledgment:
+
+   "This product includes software developed by the
+   University of Chicago, as Operator of Argonne National
+   Laboratory.
+
+Alternately, this acknowledgment may appear in the software
+itself, if and wherever such third-party acknowledgments
+normally appear.
+
+4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
+WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
+UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
+THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
+OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
+OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
+USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
+THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
+DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
+UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
+BE CORRECTED.
+
+5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
+HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
+ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
+INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
+ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
+PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
+SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
+(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
+EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
+POSSIBILITY OF SUCH LOSS OR DAMAGES.
+===============================================================================
+
+Copyright and license statement for the odex Fortran routine developed by
+E. Hairer and G. Wanner and translated in GraggBulirschStoerIntegrator class
+in package org.apache.commons.math3.ode.nonstiff:
+
+
+Copyright (c) 2004, Ernst Hairer
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are 
+met:
+
+- Redistributions of source code must retain the above copyright 
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright 
+notice, this list of conditions and the following disclaimer in the 
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+
+Copyright and license statement for the original Mersenne twister C
+routines translated in MersenneTwister class in package 
+org.apache.commons.math3.random:
+
+   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+   All rights reserved.                          
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+     1. Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+
+     2. Redistributions in binary form must reproduce the above copyright
+        notice, this list of conditions and the following disclaimer in the
+        documentation and/or other materials provided with the distribution.
+
+     3. The names of its contributors may not be used to endorse or promote 
+        products derived from this software without specific prior written 
+        permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===============================================================================
+
+The initial code for shuffling an array (originally in class
+"org.apache.commons.math3.random.RandomDataGenerator", now replaced by
+a method in class "org.apache.commons.math3.util.MathArrays") was
+inspired from the algorithm description provided in
+"Algorithms", by Ian Craw and John Pulham (University of Aberdeen 1999).
+The textbook (containing a proof that the shuffle is uniformly random) is
+available here:
+  http://citeseerx.ist.psu.edu/viewdoc/download;?doi=10.1.1.173.1898&rep=rep1&type=pdf
+
+===============================================================================
+License statement for the direction numbers in the resource files for Sobol sequences.
+
+-----------------------------------------------------------------------------
+Licence pertaining to sobol.cc and the accompanying sets of direction numbers
+
+-----------------------------------------------------------------------------
+Copyright (c) 2008, Frances Y. Kuo and Stephen Joe
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the names of the copyright holders nor the names of the
+      University of New South Wales and the University of Waikato
+      and its contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+
+The initial commit of package "org.apache.commons.math3.ml.neuralnet" is
+an adapted version of code developed in the context of the Data Processing
+and Analysis Consortium (DPAC) of the "Gaia" project of the European Space
+Agency (ESA).
+===============================================================================
+
+The initial commit of the class "org.apache.commons.math3.special.BesselJ" is
+an adapted version of code translated from the netlib Fortran program, rjbesl
+http://www.netlib.org/specfun/rjbesl by R.J. Cody at Argonne National
+Laboratory (USA).  There is no license or copyright statement included with the
+original Fortran sources.
+===============================================================================
+
+
+The BracketFinder (package org.apache.commons.math3.optimization.univariate)
+and PowellOptimizer (package org.apache.commons.math3.optimization.general)
+classes are based on the Python code in module "optimize.py" (version 0.5)
+developed by Travis E. Oliphant for the SciPy library (http://www.scipy.org/)
+Copyright © 2003-2009 SciPy Developers.
+
+SciPy license
+Copyright © 2001, 2002 Enthought, Inc.
+All rights reserved.
+
+Copyright © 2003-2013 SciPy Developers.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of Enthought nor the names of the SciPy Developers may
+      be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+
diff --git a/commons-math-transform/NOTICE b/commons-math-transform/NOTICE
new file mode 100644
index 0000000..587cd7f
--- /dev/null
+++ b/commons-math-transform/NOTICE
@@ -0,0 +1,9 @@
+Apache Commons Math
+Copyright 2001-2020 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software developed for Orekit by
+CS Systèmes d'Information (http://www.c-s.fr/)
+Copyright 2010-2012 CS Systèmes d'Information
diff --git a/commons-math-transform/pom.xml b/commons-math-transform/pom.xml
new file mode 100644
index 0000000..632ab2c
--- /dev/null
+++ b/commons-math-transform/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.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.
+-->
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-math-parent</artifactId>
+    <version>4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>commons-math4-transform</artifactId>
+  <name>Transforms</name>
+
+  <description>Fourier, Sine, Cosine, Hadamard.</description>
+
+  <properties>
+    <!-- The Java Module System Name -->
+    <commons.module.name>org.apache.commons.math4.transform</commons.module.name>
+    <!-- This value must reflect the current name of the base package. -->
+    <commons.osgi.symbolicName>org.apache.commons.math4.transform</commons.osgi.symbolicName>
+    <!-- OSGi -->
+    <commons.osgi.export>org.apache.commons.math4.transform</commons.osgi.export>
+    <!-- Workaround to avoid duplicating config files. -->
+    <math.parent.dir>${basedir}/..</math.parent.dir>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-numbers-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-numbers-complex</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-rng-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math3</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <rerunFailingTestsCount>3</rerunFailingTestsCount>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/commons-math-transform/src/main/java/org/apache/commons/math4/transform/ComplexTransform.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/ComplexTransform.java
new file mode 100644
index 0000000..68697da
--- /dev/null
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/ComplexTransform.java
@@ -0,0 +1,66 @@
+/*
+ * 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.math4.transform;
+
+import java.util.function.DoubleUnaryOperator;
+import java.util.function.UnaryOperator;
+
+import org.apache.commons.numbers.complex.Complex;
+
+/**
+ * {@link Complex} transform.
+ * <p>
+ * Such transforms include {@link FastSineTransform sine transform},
+ * {@link FastCosineTransform cosine transform} or {@link
+ * FastHadamardTransform Hadamard transform}.
+ */
+public interface ComplexTransform extends UnaryOperator<Complex[]> {
+    /**
+     * Returns the transform of the specified data set.
+     *
+     * @param f the data array to be transformed (signal).
+     * @return the transformed array (spectrum).
+     * @throws IllegalArgumentException if the transform cannot be performed.
+     */
+    Complex[] apply(Complex[] f);
+
+    /**
+     * Returns the transform of the specified data set.
+     *
+     * @param f the data array to be transformed (signal).
+     * @return the transformed array (spectrum).
+     * @throws IllegalArgumentException if the transform cannot be performed.
+     */
+    Complex[] apply(double[] f);
+
+    /**
+     * Returns the transform of the specified function.
+     *
+     * @param f Function to be sampled and transformed.
+     * @param min Lower bound (inclusive) of the interval.
+     * @param max Upper bound (exclusive) of the interval.
+     * @param n Number of sample points.
+     * @return the result.
+     * @throws IllegalArgumentException if the transform cannot be performed.
+     */
+    default Complex[] apply(DoubleUnaryOperator f,
+                            double min,
+                            double max,
+                            int n) {
+        return apply(TransformUtils.sample(f, min, max, n));
+    }
+}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DctNormalization.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/DctNormalization.java
similarity index 91%
rename from commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DctNormalization.java
rename to commons-math-transform/src/main/java/org/apache/commons/math4/transform/DctNormalization.java
index cddc688..fa77fae 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DctNormalization.java
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/DctNormalization.java
@@ -14,19 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
 /**
  * This enumeration defines the various types of normalizations that can be
- * applied to discrete cosine transforms (DCT). The exact definition of these
- * normalizations is detailed below.
- *
- * @see FastCosineTransformer
- * @since 3.0
+ * applied to discrete cosine transforms (DCT).
  */
 public enum DctNormalization {
     /**
-     * Should be passed to the constructor of {@link FastCosineTransformer}
+     * Should be passed to the constructor of {@link FastCosineTransform}
      * to use the <em>standard</em> normalization convention.  The standard
      * DCT-I normalization convention is defined as follows
      * <ul>
@@ -45,7 +41,7 @@ public enum DctNormalization {
     STANDARD_DCT_I,
 
     /**
-     * Should be passed to the constructor of {@link FastCosineTransformer}
+     * Should be passed to the constructor of {@link FastCosineTransform}
      * to use the <em>orthogonal</em> normalization convention. The orthogonal
      * DCT-I normalization convention is defined as follows
      * <ul>
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DftNormalization.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/DftNormalization.java
similarity index 96%
rename from commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DftNormalization.java
rename to commons-math-transform/src/main/java/org/apache/commons/math4/transform/DftNormalization.java
index 695feb9..1198860 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DftNormalization.java
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/DftNormalization.java
@@ -14,14 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
 /**
  * This enumeration defines the various types of normalizations that can be
  * applied to discrete Fourier transforms (DFT). The exact definition of these
  * normalizations is detailed below.
  *
- * @see FastFourierTransformer
+ * @see FastFourierTransform
  * @since 3.0
  */
 public enum DftNormalization {
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DstNormalization.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/DstNormalization.java
similarity index 90%
rename from commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DstNormalization.java
rename to commons-math-transform/src/main/java/org/apache/commons/math4/transform/DstNormalization.java
index e8dcab7..4e9ab03 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/DstNormalization.java
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/DstNormalization.java
@@ -14,19 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
 /**
  * This enumeration defines the various types of normalizations that can be
- * applied to discrete sine transforms (DST). The exact definition of these
- * normalizations is detailed below.
- *
- * @see FastSineTransformer
- * @since 3.0
+ * applied to discrete sine transforms (DST).
  */
 public enum DstNormalization {
     /**
-     * Should be passed to the constructor of {@link FastSineTransformer} to
+     * Should be passed to the constructor of {@link FastSineTransform} to
      * use the <em>standard</em> normalization convention. The standard DST-I
      * normalization convention is defined as follows
      * <ul>
@@ -40,7 +36,7 @@ public enum DstNormalization {
     STANDARD_DST_I,
 
     /**
-     * Should be passed to the constructor of {@link FastSineTransformer} to
+     * Should be passed to the constructor of {@link FastSineTransform} to
      * use the <em>orthogonal</em> normalization convention. The orthogonal
      * DCT-I normalization convention is defined as follows
      * <ul>
diff --git a/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java
new file mode 100644
index 0000000..0aa2863
--- /dev/null
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastCosineTransform.java
@@ -0,0 +1,186 @@
+/*
+ * 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.math4.transform;
+
+import java.util.function.UnaryOperator;
+import java.util.function.DoubleUnaryOperator;
+
+import org.apache.commons.numbers.complex.Complex;
+import org.apache.commons.numbers.core.ArithmeticUtils;
+
+/**
+ * Implements the Fast Cosine Transform for transformation of one-dimensional
+ * real data sets. For reference, see James S. Walker, <em>Fast Fourier
+ * Transforms</em>, chapter 3 (ISBN 0849371635).
+ * <p>
+ * There are several variants of the discrete cosine transform. The present
+ * implementation corresponds to DCT-I, with various normalization conventions,
+ * which are specified by the parameter {@link DctNormalization}.
+ * <p>
+ * DCT-I is equivalent to DFT of an <em>even extension</em> of the data series.
+ * More precisely, if x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is the data set
+ * to be cosine transformed, the extended data set
+ * x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-3</sub><sup>&#35;</sup>
+ * is defined as follows
+ * <ul>
+ * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>k</sub> if 0 &le; k &lt; N,</li>
+ * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>2N-2-k</sub>
+ * if N &le; k &lt; 2N - 2.</li>
+ * </ul>
+ * <p>
+ * Then, the standard DCT-I y<sub>0</sub>, &hellip;, y<sub>N-1</sub> of the real
+ * data set x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is equal to <em>half</em>
+ * of the N first elements of the DFT of the extended data set
+ * x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-3</sub><sup>&#35;</sup>
+ * <br>
+ * y<sub>n</sub> = (1 / 2) &sum;<sub>k=0</sub><sup>2N-3</sup>
+ * x<sub>k</sub><sup>&#35;</sup> exp[-2&pi;i nk / (2N - 2)]
+ * &nbsp;&nbsp;&nbsp;&nbsp;k = 0, &hellip;, N-1.
+ * <p>
+ * The present implementation of the discrete cosine transform as a fast cosine
+ * transform requires the length of the data set to be a power of two plus one
+ * (N&nbsp;=&nbsp;2<sup>n</sup>&nbsp;+&nbsp;1). Besides, it implicitly assumes
+ * that the sampled function is even.
+ */
+public class FastCosineTransform implements RealTransform {
+    /** Operation to be performed. */
+    private final UnaryOperator<double[]> op;
+
+    /**
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     * @param inverse Whether to perform the inverse transform.
+     */
+    public FastCosineTransform(final DctNormalization normalization,
+                               final boolean inverse) {
+        op = create(normalization, inverse);
+    }
+
+    /**
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     */
+    public FastCosineTransform(final DctNormalization normalization) {
+        this(normalization, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two plus one.
+     */
+    @Override
+    public double[] apply(final double[] f) {
+        return op.apply(f);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the number of sample points is
+     * not a power of two plus one, if the lower bound is greater than or
+     * equal to the upper bound, if the number of sample points is negative.
+     */
+    @Override
+    public double[] apply(final DoubleUnaryOperator f,
+                          final double min,
+                          final double max,
+                          final int n) {
+        return apply(TransformUtils.sample(f, min, max, n));
+    }
+
+    /**
+     * Perform the FCT algorithm (including inverse).
+     *
+     * @param f Data to be transformed.
+     * @return the transformed array.
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two plus one.
+     */
+    private double[] fct(double[] f) {
+        final double[] transformed = new double[f.length];
+
+        final int n = f.length - 1;
+        if (!ArithmeticUtils.isPowerOfTwo(n)) {
+            throw new TransformException(TransformException.NOT_POWER_OF_TWO_PLUS_ONE,
+                                         Integer.valueOf(f.length));
+        }
+        if (n == 1) {       // trivial case
+            transformed[0] = 0.5 * (f[0] + f[1]);
+            transformed[1] = 0.5 * (f[0] - f[1]);
+            return transformed;
+        }
+
+        // construct a new array and perform FFT on it
+        final double[] x = new double[n];
+        x[0] = 0.5 * (f[0] + f[n]);
+        final int nShifted = n >> 1;
+        x[nShifted] = f[nShifted];
+        // temporary variable for transformed[1]
+        double t1 = 0.5 * (f[0] - f[n]);
+        final double piOverN = Math.PI / n;
+        for (int i = 1; i < nShifted; i++) {
+            final int nMi = n - i;
+            final double fi = f[i];
+            final double fnMi = f[nMi];
+            final double a = 0.5 * (fi + fnMi);
+            final double arg = i * piOverN;
+            final double b = Math.sin(arg) * (fi - fnMi);
+            final double c = Math.cos(arg) * (fi - fnMi);
+            x[i] = a - b;
+            x[nMi] = a + b;
+            t1 += c;
+        }
+        final FastFourierTransform transformer = new FastFourierTransform(DftNormalization.STANDARD,
+                                                                          false);
+        final Complex[] y = transformer.apply(x);
+
+        // reconstruct the FCT result for the original array
+        transformed[0] = y[0].getReal();
+        transformed[1] = t1;
+        for (int i = 1; i < nShifted; i++) {
+            final int i2 = 2 * i;
+            transformed[i2] = y[i].getReal();
+            transformed[i2 + 1] = transformed[i2 - 1] - y[i].getImaginary();
+        }
+        transformed[n] = y[nShifted].getReal();
+
+        return transformed;
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     * @param inverse Whether to perform the inverse transform.
+     * @return the transform operator.
+     */
+    private UnaryOperator<double[]> create(final DctNormalization normalization,
+                                           final boolean inverse) {
+        if (inverse) {
+            return normalization == DctNormalization.ORTHOGONAL_DCT_I ?
+                (f) -> TransformUtils.scaleInPlace(fct(f), Math.sqrt(2d / (f.length - 1))) :
+                (f) -> TransformUtils.scaleInPlace(fct(f), 2d / (f.length - 1));
+        } else {
+            return normalization == DctNormalization.ORTHOGONAL_DCT_I ?
+                (f) -> TransformUtils.scaleInPlace(fct(f), Math.sqrt(2d / (f.length - 1))) :
+                (f) -> fct(f);
+        }
+    }
+}
diff --git a/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastFourierTransform.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastFourierTransform.java
new file mode 100644
index 0000000..be704af
--- /dev/null
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastFourierTransform.java
@@ -0,0 +1,397 @@
+/*
+ * 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.math4.transform;
+
+import java.util.Arrays;
+import java.util.function.DoubleUnaryOperator;
+
+import org.apache.commons.numbers.core.ArithmeticUtils;
+import org.apache.commons.numbers.complex.Complex;
+
+/**
+ * Implements the Fast Fourier Transform for transformation of one-dimensional
+ * real or complex data sets. For reference, see <em>Applied Numerical Linear
+ * Algebra</em>, ISBN 0898713897, chapter 6.
+ * <p>
+ * There are several variants of the discrete Fourier transform, with various
+ * normalization conventions, which are specified by the parameter
+ * {@link DftNormalization}.
+ * <p>
+ * The current implementation of the discrete Fourier transform as a fast
+ * Fourier transform requires the length of the data set to be a power of 2.
+ * This greatly simplifies and speeds up the code. Users can pad the data with
+ * zeros to meet this requirement. There are other flavors of FFT, for
+ * reference, see S. Winograd,
+ * <i>On computing the discrete Fourier transform</i>, Mathematics of
+ * Computation, 32 (1978), 175 - 199.
+ *
+ * @see DftNormalization
+ */
+public class FastFourierTransform implements ComplexTransform {
+    /**
+     * {@code W_SUB_N_R[i]} is the real part of
+     * {@code exp(- 2 * i * pi / n)}:
+     * {@code W_SUB_N_R[i] = cos(2 * pi/ n)}, where {@code n = 2^i}.
+     */
+    private static final double[] W_SUB_N_R = {
+        0x1.0p0, -0x1.0p0, 0x1.1a62633145c07p-54, 0x1.6a09e667f3bcdp-1,
+        0x1.d906bcf328d46p-1, 0x1.f6297cff75cbp-1, 0x1.fd88da3d12526p-1, 0x1.ff621e3796d7ep-1,
+        0x1.ffd886084cd0dp-1, 0x1.fff62169b92dbp-1, 0x1.fffd8858e8a92p-1, 0x1.ffff621621d02p-1,
+        0x1.ffffd88586ee6p-1, 0x1.fffff62161a34p-1, 0x1.fffffd8858675p-1, 0x1.ffffff621619cp-1,
+        0x1.ffffffd885867p-1, 0x1.fffffff62161ap-1, 0x1.fffffffd88586p-1, 0x1.ffffffff62162p-1,
+        0x1.ffffffffd8858p-1, 0x1.fffffffff6216p-1, 0x1.fffffffffd886p-1, 0x1.ffffffffff621p-1,
+        0x1.ffffffffffd88p-1, 0x1.fffffffffff62p-1, 0x1.fffffffffffd9p-1, 0x1.ffffffffffff6p-1,
+        0x1.ffffffffffffep-1, 0x1.fffffffffffffp-1, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
+        0x1.0p0, 0x1.0p0, 0x1.0p0 };
+
+    /**
+     * {@code W_SUB_N_I[i]} is the imaginary part of
+     * {@code exp(- 2 * i * pi / n)}:
+     * {@code W_SUB_N_I[i] = -sin(2 * pi/ n)}, where {@code n = 2^i}.
+     */
+    private static final double[] W_SUB_N_I = {
+        0x1.1a62633145c07p-52, -0x1.1a62633145c07p-53, -0x1.0p0, -0x1.6a09e667f3bccp-1,
+        -0x1.87de2a6aea963p-2, -0x1.8f8b83c69a60ap-3, -0x1.917a6bc29b42cp-4, -0x1.91f65f10dd814p-5,
+        -0x1.92155f7a3667ep-6, -0x1.921d1fcdec784p-7, -0x1.921f0fe670071p-8, -0x1.921f8becca4bap-9,
+        -0x1.921faaee6472dp-10, -0x1.921fb2aecb36p-11, -0x1.921fb49ee4ea6p-12, -0x1.921fb51aeb57bp-13,
+        -0x1.921fb539ecf31p-14, -0x1.921fb541ad59ep-15, -0x1.921fb5439d73ap-16, -0x1.921fb544197ap-17,
+        -0x1.921fb544387bap-18, -0x1.921fb544403c1p-19, -0x1.921fb544422c2p-20, -0x1.921fb54442a83p-21,
+        -0x1.921fb54442c73p-22, -0x1.921fb54442cefp-23, -0x1.921fb54442d0ep-24, -0x1.921fb54442d15p-25,
+        -0x1.921fb54442d17p-26, -0x1.921fb54442d18p-27, -0x1.921fb54442d18p-28, -0x1.921fb54442d18p-29,
+        -0x1.921fb54442d18p-30, -0x1.921fb54442d18p-31, -0x1.921fb54442d18p-32, -0x1.921fb54442d18p-33,
+        -0x1.921fb54442d18p-34, -0x1.921fb54442d18p-35, -0x1.921fb54442d18p-36, -0x1.921fb54442d18p-37,
+        -0x1.921fb54442d18p-38, -0x1.921fb54442d18p-39, -0x1.921fb54442d18p-40, -0x1.921fb54442d18p-41,
+        -0x1.921fb54442d18p-42, -0x1.921fb54442d18p-43, -0x1.921fb54442d18p-44, -0x1.921fb54442d18p-45,
+        -0x1.921fb54442d18p-46, -0x1.921fb54442d18p-47, -0x1.921fb54442d18p-48, -0x1.921fb54442d18p-49,
+        -0x1.921fb54442d18p-50, -0x1.921fb54442d18p-51, -0x1.921fb54442d18p-52, -0x1.921fb54442d18p-53,
+        -0x1.921fb54442d18p-54, -0x1.921fb54442d18p-55, -0x1.921fb54442d18p-56, -0x1.921fb54442d18p-57,
+        -0x1.921fb54442d18p-58, -0x1.921fb54442d18p-59, -0x1.921fb54442d18p-60 };
+
+    /** Type of DFT. */
+    private final DftNormalization normalization;
+    /** Inverse or forward. */
+    private final boolean inverse;
+
+    /**
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     * @param inverse Whether to perform the inverse transform.
+     */
+    public FastFourierTransform(final DftNormalization normalization,
+                                final boolean inverse) {
+        this.normalization = normalization;
+        this.inverse = inverse;
+    }
+
+    /**
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     */
+    public FastFourierTransform(final DftNormalization normalization) {
+        this(normalization, false);
+    }
+
+    /**
+     * Computes the standard transform of the data.
+     * Computation is done in place.
+     * Assumed layout of the input data:
+     * <ul>
+     *   <li>{@code dataRI[0][i]}: Real part of the {@code i}-th data point,</li>
+     *   <li>{@code dataRI[1][i]}: Imaginary part of the {@code i}-th data point.</li>
+     * </ul>
+     *
+     * @param dataRI Two-dimensional array of real and imaginary parts of the data.
+     * @throws IllegalArgumentException if the number of data points is not
+     * a power of two, if the number of rows of the specified array is not two,
+     * or the array is not rectangular.
+     */
+    public void transformInPlace(final double[][] dataRI) {
+        if (dataRI.length != 2) {
+            throw new TransformException(TransformException.SIZE_MISMATCH,
+                                         dataRI.length, 2);
+        }
+        final double[] dataR = dataRI[0];
+        final double[] dataI = dataRI[1];
+        if (dataR.length != dataI.length) {
+            throw new TransformException(TransformException.SIZE_MISMATCH,
+                                         dataI.length, dataR.length);
+        }
+
+        final int n = dataR.length;
+        if (!ArithmeticUtils.isPowerOfTwo(n)) {
+            throw new TransformException(TransformException.NOT_POWER_OF_TWO,
+                                         Integer.valueOf(n));
+        }
+
+        if (n == 1) {
+            return;
+        } else if (n == 2) {
+            final double srcR0 = dataR[0];
+            final double srcI0 = dataI[0];
+            final double srcR1 = dataR[1];
+            final double srcI1 = dataI[1];
+
+            // X_0 = x_0 + x_1
+            dataR[0] = srcR0 + srcR1;
+            dataI[0] = srcI0 + srcI1;
+            // X_1 = x_0 - x_1
+            dataR[1] = srcR0 - srcR1;
+            dataI[1] = srcI0 - srcI1;
+
+            normalizeTransformedData(dataRI);
+            return;
+        }
+
+        bitReversalShuffle2(dataR, dataI);
+
+        // Do 4-term DFT.
+        if (inverse) {
+            for (int i0 = 0; i0 < n; i0 += 4) {
+                final int i1 = i0 + 1;
+                final int i2 = i0 + 2;
+                final int i3 = i0 + 3;
+
+                final double srcR0 = dataR[i0];
+                final double srcI0 = dataI[i0];
+                final double srcR1 = dataR[i2];
+                final double srcI1 = dataI[i2];
+                final double srcR2 = dataR[i1];
+                final double srcI2 = dataI[i1];
+                final double srcR3 = dataR[i3];
+                final double srcI3 = dataI[i3];
+
+                // 4-term DFT
+                // X_0 = x_0 + x_1 + x_2 + x_3
+                dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
+                dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
+                // X_1 = x_0 - x_2 + j * (x_3 - x_1)
+                dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1);
+                dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3);
+                // X_2 = x_0 - x_1 + x_2 - x_3
+                dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
+                dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
+                // X_3 = x_0 - x_2 + j * (x_1 - x_3)
+                dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3);
+                dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1);
+            }
+        } else {
+            for (int i0 = 0; i0 < n; i0 += 4) {
+                final int i1 = i0 + 1;
+                final int i2 = i0 + 2;
+                final int i3 = i0 + 3;
+
+                final double srcR0 = dataR[i0];
+                final double srcI0 = dataI[i0];
+                final double srcR1 = dataR[i2];
+                final double srcI1 = dataI[i2];
+                final double srcR2 = dataR[i1];
+                final double srcI2 = dataI[i1];
+                final double srcR3 = dataR[i3];
+                final double srcI3 = dataI[i3];
+
+                // 4-term DFT
+                // X_0 = x_0 + x_1 + x_2 + x_3
+                dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
+                dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
+                // X_1 = x_0 - x_2 + j * (x_3 - x_1)
+                dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3);
+                dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1);
+                // X_2 = x_0 - x_1 + x_2 - x_3
+                dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
+                dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
+                // X_3 = x_0 - x_2 + j * (x_1 - x_3)
+                dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1);
+                dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3);
+            }
+        }
+
+        int lastN0 = 4;
+        int lastLogN0 = 2;
+        while (lastN0 < n) {
+            int n0 = lastN0 << 1;
+            int logN0 = lastLogN0 + 1;
+            double wSubN0R = W_SUB_N_R[logN0];
+            double wSubN0I = W_SUB_N_I[logN0];
+            if (inverse) {
+                wSubN0I = -wSubN0I;
+            }
+
+            // Combine even/odd transforms of size lastN0 into a transform of size N0 (lastN0 * 2).
+            for (int destEvenStartIndex = 0;
+                 destEvenStartIndex < n;
+                 destEvenStartIndex += n0) {
+                final int destOddStartIndex = destEvenStartIndex + lastN0;
+
+                double wSubN0ToRR = 1;
+                double wSubN0ToRI = 0;
+
+                for (int r = 0; r < lastN0; r++) {
+                    final int destEvenStartIndexPlusR = destEvenStartIndex + r;
+                    final int destOddStartIndexPlusR = destOddStartIndex + r;
+
+                    final double grR = dataR[destEvenStartIndexPlusR];
+                    final double grI = dataI[destEvenStartIndexPlusR];
+                    final double hrR = dataR[destOddStartIndexPlusR];
+                    final double hrI = dataI[destOddStartIndexPlusR];
+
+                    // dest[destEvenStartIndex + r] = Gr + WsubN0ToR * Hr
+                    dataR[destEvenStartIndexPlusR] = grR + wSubN0ToRR * hrR - wSubN0ToRI * hrI;
+                    dataI[destEvenStartIndexPlusR] = grI + wSubN0ToRR * hrI + wSubN0ToRI * hrR;
+                    // dest[destOddStartIndex + r] = Gr - WsubN0ToR * Hr
+                    dataR[destOddStartIndexPlusR] = grR - (wSubN0ToRR * hrR - wSubN0ToRI * hrI);
+                    dataI[destOddStartIndexPlusR] = grI - (wSubN0ToRR * hrI + wSubN0ToRI * hrR);
+
+                    // WsubN0ToR *= WsubN0R
+                    final double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I;
+                    final double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R;
+                    wSubN0ToRR = nextWsubN0ToRR;
+                    wSubN0ToRI = nextWsubN0ToRI;
+                }
+            }
+
+            lastN0 = n0;
+            lastLogN0 = logN0;
+        }
+
+        normalizeTransformedData(dataRI);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the length of the data array is not a power of two.
+     */
+    public Complex[] apply(final double[] f) {
+        final double[][] dataRI = new double[][] {
+            Arrays.copyOf(f, f.length),
+            new double[f.length]
+        };
+        transformInPlace(dataRI);
+        return TransformUtils.createComplex(dataRI);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the number of sample points
+     * {@code n} is not a power of two, if the lower bound is greater than,
+     * or equal to the upper bound, if the number of sample points {@code n}
+     * is negative
+     */
+    @Override
+    public Complex[] apply(final DoubleUnaryOperator f,
+                           final double min,
+                           final double max,
+                           final int n) {
+        return apply(TransformUtils.sample(f, min, max, n));
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two.
+     */
+    @Override
+    public Complex[] apply(final Complex[] f) {
+        final double[][] dataRI = TransformUtils.createRealImaginary(f);
+        transformInPlace(dataRI);
+        return TransformUtils.createComplex(dataRI);
+    }
+
+    /**
+     * Applies normalization to the transformed data.
+     *
+     * @param dataRI Unscaled transformed data.
+     */
+    private void normalizeTransformedData(final double[][] dataRI) {
+        final double[] dataR = dataRI[0];
+        final double[] dataI = dataRI[1];
+        final int n = dataR.length;
+
+        switch (normalization) {
+        case STANDARD:
+            if (inverse) {
+                final double scaleFactor = 1d / n;
+                for (int i = 0; i < n; i++) {
+                    dataR[i] *= scaleFactor;
+                    dataI[i] *= scaleFactor;
+                }
+            }
+
+            break;
+
+        case UNITARY:
+            final double scaleFactor = 1d / Math.sqrt(n);
+            for (int i = 0; i < n; i++) {
+                dataR[i] *= scaleFactor;
+                dataI[i] *= scaleFactor;
+            }
+
+            break;
+
+        default:
+            throw new IllegalStateException(); // Should never happen.
+        }
+    }
+
+    /**
+     * Performs identical index bit reversal shuffles on two arrays of
+     * identical size.
+     * Each element in the array is swapped with another element based
+     * on the bit-reversal of the index.
+     * For example, in an array with length 16, item at binary index 0011
+     * (decimal 3) would be swapped with the item at binary index 1100
+     * (decimal 12).
+     *
+     * @param a Array to be shuffled.
+     * @param b Array to be shuffled.
+     */
+    private static void bitReversalShuffle2(double[] a,
+                                            double[] b) {
+        final int n = a.length;
+        final int halfOfN = n >> 1;
+
+        int j = 0;
+        for (int i = 0; i < n; i++) {
+            if (i < j) {
+                // swap indices i & j
+                double temp = a[i];
+                a[i] = a[j];
+                a[j] = temp;
+
+                temp = b[i];
+                b[i] = b[j];
+                b[j] = temp;
+            }
+
+            int k = halfOfN;
+            while (k <= j && k > 0) {
+                j -= k;
+                k >>= 1;
+            }
+            j += k;
+        }
+    }
+}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastHadamardTransformer.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastHadamardTransform.java
similarity index 67%
rename from commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastHadamardTransformer.java
rename to commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastHadamardTransform.java
index 90a3aa8..ab37285 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/FastHadamardTransformer.java
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastHadamardTransform.java
@@ -14,80 +14,66 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
-import java.io.Serializable;
+import java.util.function.UnaryOperator;
 
-import org.apache.commons.math4.legacy.analysis.FunctionUtils;
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
 import org.apache.commons.numbers.core.ArithmeticUtils;
 
 /**
- * Implements the <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">Fast Hadamard Transform</a> (FHT).
- * Transformation of an input vector x to the output vector y.
+ * <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">Fast Hadamard Transform</a> (FHT).
  * <p>
- * In addition to transformation of real vectors, the Hadamard transform can
- * transform integer vectors into integer vectors. However, this integer transform
- * cannot be inverted directly. Due to a scaling factor it may lead to rational results.
- * As an example, the inverse transform of integer vector (0, 1, 0, 1) is rational
- * vector (1/2, -1/2, 0, 0).
- *
- * @since 2.0
+ * The FHT can also transform integer vectors into integer vectors.
+ * However, this transform cannot be inverted directly, due to a scaling
+ * factor that may lead to rational results (for example, the inverse
+ * transform of integer vector (0, 1, 0, 1) is vector (1/2, -1/2, 0, 0).
  */
-public class FastHadamardTransformer implements RealTransformer, Serializable {
+public class FastHadamardTransform implements RealTransform {
+    /** Operation to be performed. */
+    private final UnaryOperator<double[]> op;
 
-    /** Serializable version identifier. */
-    static final long serialVersionUID = 20120211L;
+    /**
+     * Default constructor.
+     */
+    public FastHadamardTransform() {
+        this(false);
+    }
 
     /**
-     * {@inheritDoc}
-     *
-     * @throws MathIllegalArgumentException if the length of the data array is
-     * not a power of two
+     * @param inverse Whether to perform the inverse transform.
      */
-    @Override
-    public double[] transform(final double[] f, final TransformType type) {
-        if (type == TransformType.FORWARD) {
-            return fht(f);
-        }
-        return TransformUtils.scaleArray(fht(f), 1.0 / f.length);
+    public FastHadamardTransform(final boolean inverse) {
+        op = create(inverse);
     }
 
     /**
      * {@inheritDoc}
      *
-     * @throws org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException
-     *   if the lower bound is greater than, or equal to the upper bound
-     * @throws org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException
-     *   if the number of sample points is negative
-     * @throws MathIllegalArgumentException if the number of sample points is not a power of two
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two.
      */
     @Override
-    public double[] transform(final UnivariateFunction f,
-        final double min, final double max, final int n,
-        final TransformType type) {
-
-        return transform(FunctionUtils.sample(f, min, max, n), type);
+    public double[] apply(final double[] f) {
+        return op.apply(f);
     }
 
     /**
-     * Returns the forward transform of the specified integer data set.The
-     * integer transform cannot be inverted directly, due to a scaling factor
-     * which may lead to double results.
+     * Returns the forward transform of the given data set.
+     * The integer transform cannot be inverted directly, due to a scaling
+     * factor which may lead to double results.
      *
-     * @param f the integer data array to be transformed (signal)
-     * @return the integer transformed array (spectrum)
-     * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+     * @param f Data array to be transformed (signal).
+     * @return the transformed array (spectrum).
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two.
      */
-    public int[] transform(final int[] f) {
+    public int[] apply(final int[] f) {
         return fht(f);
     }
 
     /**
-     * The FHT (Fast Hadamard Transformation) which uses only subtraction and
-     * addition. Requires {@code N * log2(N) = n * 2^n} additions.
+     * FHT uses only subtraction and addition.
+     * It requires {@code N * log2(N) = n * 2^n} additions.
      *
      * <h3>Short Table of manual calculation for N=8</h3>
      * <ol>
@@ -180,7 +166,7 @@ public class FastHadamardTransformer implements RealTransformer, Serializable {
      *         of the previous column.</li>
      *     </ul>
      * </li>
-     * <li>The consputation of {@code D_top} and {@code D_bottom} are best
+     * <li>The computation of {@code D_top} and {@code D_bottom} are best
      * understood with the above example (for {@code N = 8}).
      * <li>The output vector {@code y} is now in the last column of
      * {@code hadm}.</li>
@@ -223,25 +209,22 @@ public class FastHadamardTransformer implements RealTransformer, Serializable {
      * </tbody>
      * </table>
      *
-     * @param x the real data array to be transformed
-     * @return the real transformed array, {@code y}
-     * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+     * @param x Data to be transformed.
+     * @return the transformed array.
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two.
      */
-    protected double[] fht(double[] x) throws MathIllegalArgumentException {
-
-        final int n     = x.length;
+    private double[] fht(double[] x) {
+        final int n = x.length;
         final int halfN = n / 2;
 
         if (!ArithmeticUtils.isPowerOfTwo(n)) {
-            throw new MathIllegalArgumentException(
-                    LocalizedFormats.NOT_POWER_OF_TWO,
-                    Integer.valueOf(n));
+            throw new TransformException(TransformException.NOT_POWER_OF_TWO,
+                                         n);
         }
 
-        /*
-         * Instead of creating a matrix with p+1 columns and n rows, we use two
-         * one dimension arrays which we are used in an alternating way.
-         */
+        // Instead of creating a matrix with p+1 columns and n rows, we use two
+        // one dimension arrays which we are used in an alternating way.
         double[] yPrevious = new double[n];
         double[] yCurrent  = x.clone();
 
@@ -267,38 +250,34 @@ public class FastHadamardTransformer implements RealTransformer, Serializable {
         }
 
         return yCurrent;
-
     }
 
     /**
-     * Returns the forward transform of the specified integer data set. The FHT
-     * (Fast Hadamard Transform) uses only subtraction and addition.
+     * Returns the forward transform of the specified integer data set.
+     * FHT only uses subtraction and addition.
      *
-     * @param x the integer data array to be transformed
-     * @return the integer transformed array, {@code y}
-     * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+     * @param x Data to be transformed.
+     * @return the transformed array.
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two.
      */
-    protected int[] fht(int[] x) throws MathIllegalArgumentException {
-
-        final int n     = x.length;
+    private int[] fht(int[] x) {
+        final int n = x.length;
         final int halfN = n / 2;
 
         if (!ArithmeticUtils.isPowerOfTwo(n)) {
-            throw new MathIllegalArgumentException(
-                    LocalizedFormats.NOT_POWER_OF_TWO,
-                    Integer.valueOf(n));
+            throw new TransformException(TransformException.NOT_POWER_OF_TWO,
+                                         n);
         }
 
-        /*
-         * Instead of creating a matrix with p+1 columns and n rows, we use two
-         * one dimension arrays which we are used in an alternating way.
-         */
+        // Instead of creating a matrix with p+1 columns and n rows, we use two
+        // one dimension arrays which we are used in an alternating way.
+
         int[] yPrevious = new int[n];
         int[] yCurrent  = x.clone();
 
         // iterate from left to right (column)
         for (int j = 1; j < n; j <<= 1) {
-
             // switch columns
             final int[] yTmp = yCurrent;
             yCurrent  = yPrevious;
@@ -319,7 +298,19 @@ public class FastHadamardTransformer implements RealTransformer, Serializable {
 
         // return the last computed output vector y
         return yCurrent;
-
     }
 
+    /**
+     * Factory method.
+     *
+     * @param inverse Whether to perform the inverse transform.
+     * @return the transform operator.
+     */
+    private UnaryOperator<double[]> create(final boolean inverse) {
+        if (inverse) {
+            return (f) -> TransformUtils.scaleInPlace(fht(f), 1d / f.length);
+        } else {
+            return (f) -> fht(f);
+        }
+    }
 }
diff --git a/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastSineTransform.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastSineTransform.java
new file mode 100644
index 0000000..da3c63b
--- /dev/null
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/FastSineTransform.java
@@ -0,0 +1,194 @@
+/*
+ * 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.math4.transform;
+
+import java.util.function.UnaryOperator;
+import java.util.function.DoubleUnaryOperator;
+
+import org.apache.commons.numbers.complex.Complex;
+import org.apache.commons.numbers.core.ArithmeticUtils;
+
+/**
+ * Implements the Fast Sine Transform for transformation of one-dimensional real
+ * data sets. For reference, see James S. Walker, <em>Fast Fourier
+ * Transforms</em>, chapter 3 (ISBN 0849371635).
+ * <p>
+ * There are several variants of the discrete sine transform. The present
+ * implementation corresponds to DST-I, with various normalization conventions,
+ * which are specified by the parameter {@link DstNormalization}.
+ * <strong>It should be noted that regardless to the convention, the first
+ * element of the dataset to be transformed must be zero.</strong>
+ * <p>
+ * DST-I is equivalent to DFT of an <em>odd extension</em> of the data series.
+ * More precisely, if x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is the data set
+ * to be sine transformed, the extended data set x<sub>0</sub><sup>&#35;</sup>,
+ * &hellip;, x<sub>2N-1</sub><sup>&#35;</sup> is defined as follows
+ * <ul>
+ * <li>x<sub>0</sub><sup>&#35;</sup> = x<sub>0</sub> = 0,</li>
+ * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>k</sub> if 1 &le; k &lt; N,</li>
+ * <li>x<sub>N</sub><sup>&#35;</sup> = 0,</li>
+ * <li>x<sub>k</sub><sup>&#35;</sup> = -x<sub>2N-k</sub> if N + 1 &le; k &lt;
+ * 2N.</li>
+ * </ul>
+ * <p>
+ * Then, the standard DST-I y<sub>0</sub>, &hellip;, y<sub>N-1</sub> of the real
+ * data set x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is equal to <em>half</em>
+ * of i (the pure imaginary number) times the N first elements of the DFT of the
+ * extended data set x<sub>0</sub><sup>&#35;</sup>, &hellip;,
+ * x<sub>2N-1</sub><sup>&#35;</sup> <br>
+ * y<sub>n</sub> = (i / 2) &sum;<sub>k=0</sub><sup>2N-1</sup>
+ * x<sub>k</sub><sup>&#35;</sup> exp[-2&pi;i nk / (2N)]
+ * &nbsp;&nbsp;&nbsp;&nbsp;k = 0, &hellip;, N-1.
+ * <p>
+ * The present implementation of the discrete sine transform as a fast sine
+ * transform requires the length of the data to be a power of two. Besides,
+ * it implicitly assumes that the sampled function is odd. In particular, the
+ * first element of the data set must be 0, which is enforced in
+ * {@link #apply(DoubleUnaryOperator, double, double, int)},
+ * after sampling.
+ */
+public class FastSineTransform implements RealTransform {
+    /** Operation to be performed. */
+    private final UnaryOperator<double[]> op;
+
+    /**
+     * @param normalization Normalization to be applied to the transformed data.
+     * @param inverse Whether to perform the inverse transform.
+     */
+    public FastSineTransform(final DstNormalization normalization,
+                             final boolean inverse) {
+        op = create(normalization, inverse);
+    }
+
+    /**
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     */
+    public FastSineTransform(final DstNormalization normalization) {
+        this(normalization, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * The first element of the specified data set is required to be {@code 0}.
+     *
+     * @throws MathIllegalArgumentException if the length of the data array is
+     * not a power of two, or the first element of the data array is not zero.
+     */
+    @Override
+    public double[] apply(final double[] f) {
+        return op.apply(f);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * The implementation enforces {@code f(x) = 0} at {@code x = 0}.
+     *
+     * @throws IllegalArgumentException if the number of sample points is not a
+     * power of two, if the lower bound is greater than, or equal to the upper bound,
+     * if the number of sample points is negative.
+     */
+    @Override
+    public double[] apply(final DoubleUnaryOperator f,
+                          final double min,
+                          final double max,
+                          final int n) {
+        final double[] data = TransformUtils.sample(f, min, max, n);
+        data[0] = 0;
+        return apply(data);
+    }
+
+    /**
+     * Perform the FST algorithm (including inverse).
+     * The first element of the data set is required to be {@code 0}.
+     *
+     * @param f Data array to be transformed.
+     * @return the transformed array.
+     * @throws IllegalArgumentException if the length of the data array is
+     * not a power of two, or the first element of the data array is not zero.
+     */
+    private double[] fst(double[] f) {
+        if (!ArithmeticUtils.isPowerOfTwo(f.length)) {
+            throw new TransformException(TransformException.NOT_POWER_OF_TWO,
+                                         f.length);
+        }
+        if (f[0] != 0) {
+            throw new TransformException(TransformException.FIRST_ELEMENT_NOT_ZERO,
+                                         f[0]);
+        }
+
+        final double[] transformed = new double[f.length];
+        final int n = f.length;
+        if (n == 1) {
+            transformed[0] = 0;
+            return transformed;
+        }
+
+        // construct a new array and perform FFT on it
+        final double[] x = new double[n];
+        x[0] = 0;
+        final int nShifted = n >> 1;
+        x[nShifted] = 2 * f[nShifted];
+        final double piOverN = Math.PI / n;
+        for (int i = 1; i < nShifted; i++) {
+            final int nMi = n - i;
+            final double fi = f[i];
+            final double fnMi = f[nMi];
+            final double a = Math.sin(i * piOverN) * (fi + fnMi);
+            final double b = 0.5 * (fi - fnMi);
+            x[i] = a + b;
+            x[nMi] = a - b;
+        }
+
+        final FastFourierTransform transform = new FastFourierTransform(DftNormalization.STANDARD);
+        final Complex[] y = transform.apply(x);
+
+        // reconstruct the FST result for the original array
+        transformed[0] = 0;
+        transformed[1] = 0.5 * y[0].getReal();
+        for (int i = 1; i < nShifted; i++) {
+            final int i2 = 2 * i;
+            transformed[i2] = -y[i].getImaginary();
+            transformed[i2 + 1] = y[i].getReal() + transformed[i2 - 1];
+        }
+
+        return transformed;
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param normalization Normalization to be applied to the
+     * transformed data.
+     * @param inverse Whether to perform the inverse transform.
+     * @return the transform operator.
+     */
+    private UnaryOperator<double[]> create(final DstNormalization normalization,
+                                           final boolean inverse) {
+        if (inverse) {
+            return normalization == DstNormalization.ORTHOGONAL_DST_I ?
+                (f) -> TransformUtils.scaleInPlace(fst(f), Math.sqrt(2d / f.length)) :
+                (f) -> TransformUtils.scaleInPlace(fst(f), 2d / f.length);
+        } else {
+            return normalization == DstNormalization.ORTHOGONAL_DST_I ?
+                (f) -> TransformUtils.scaleInPlace(fst(f), Math.sqrt(2d / f.length)) :
+                (f) -> fst(f);
+        }
+    }
+}
diff --git a/commons-math-transform/src/main/java/org/apache/commons/math4/transform/RealTransform.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/RealTransform.java
new file mode 100644
index 0000000..8f8f6af
--- /dev/null
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/RealTransform.java
@@ -0,0 +1,51 @@
+/*
+ * 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.math4.transform;
+
+import java.util.function.DoubleUnaryOperator;
+import java.util.function.UnaryOperator;
+
+/**
+ * Real transforms.
+ */
+public interface RealTransform extends UnaryOperator<double[]> {
+    /**
+     * Returns the transform of the specified data set.
+     *
+     * @param f the data array to be transformed (signal).
+     * @return the transformed array (spectrum).
+     * @throws IllegalArgumentException if the transform cannot be performed.
+     */
+    double[] apply(double[] f);
+
+    /**
+     * Returns the transform of the specified function.
+     *
+     * @param f Function to be sampled and transformed.
+     * @param min Lower bound (inclusive) of the interval.
+     * @param max Upper bound (exclusive) of the interval.
+     * @param n Number of sample points.
+     * @return the result.
+     * @throws IllegalArgumentException if the transform cannot be performed.
+     */
+    default double[] apply(DoubleUnaryOperator f,
+                           double min,
+                           double max,
+                           int n) {
+        return apply(TransformUtils.sample(f, min, max, n));
+    }
+}
diff --git a/commons-math-transform/src/main/java/org/apache/commons/math4/transform/TransformException.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/TransformException.java
new file mode 100644
index 0000000..0c298ee
--- /dev/null
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/TransformException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.math4.transform;
+
+import java.text.MessageFormat;
+
+/**
+ * Exception class with constants for frequently used messages.
+ * Class is package-private (for internal use only).
+ */
+class TransformException extends IllegalArgumentException {
+    /** Error message for "out of range" condition. */
+    public static final String FIRST_ELEMENT_NOT_ZERO = "First element ({0}) must be 0";
+    /** Error message for "not strictly positive" condition. */
+    public static final String NOT_STRICTLY_POSITIVE = "Number {0} is not strictly positive";
+    /** Error message for "too large" condition. */
+    public static final String TOO_LARGE = "Number {0} is larger than {1}";
+    /** Error message for "size mismatch" condition. */
+    public static final String SIZE_MISMATCH = "Size mismatch: {0} != {1}";
+    /** Error message for "pow(2, n) + 1". */
+    public static final String NOT_POWER_OF_TWO_PLUS_ONE = "{0} is not equal to 1 + pow(2, n), for some n";
+    /** Error message for "pow(2, n)". */
+    public static final String NOT_POWER_OF_TWO = "{0} is not equal to pow(2, n), for some n";
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 20210522L;
+
+    /**
+     * Create an exception where the message is constructed by applying
+     * the {@code format()} method from {@code java.text.MessageFormat}.
+     *
+     * @param message Message format (with replaceable parameters).
+     * @param formatArguments Actual arguments to be displayed in the message.
+     */
+    public TransformException(String message, Object... formatArguments) {
+        super(MessageFormat.format(message, formatArguments));
+    }
+}
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/TransformUtils.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/TransformUtils.java
similarity index 52%
rename from commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/TransformUtils.java
rename to commons-math-transform/src/main/java/org/apache/commons/math4/transform/TransformUtils.java
index 09d4e8a..bf1fa5e 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/TransformUtils.java
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/TransformUtils.java
@@ -14,50 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
 import java.util.Arrays;
+import java.util.function.DoubleUnaryOperator;
 
 import org.apache.commons.numbers.complex.Complex;
-import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
 
 /**
  * Useful functions for the implementation of various transforms.
- *
- * @since 3.0
+ * Class is package-private (for internal use only).
  */
-public class TransformUtils {
-    /**
-     * Table of the powers of 2 to facilitate binary search lookup.
-     *
-     * @see #exactLog2(int)
-     */
-    private static final int[] POWERS_OF_TWO = {
-        0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
-        0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800,
-        0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
-        0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000,
-        0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
-        0x40000000
-    };
-
-    /** Private constructor. */
-    private TransformUtils() {
-        super();
-    }
+class TransformUtils {
+    /** Utility class. */
+    private TransformUtils() {}
 
     /**
      * Multiply every component in the given real array by the
      * given real number. The change is made in place.
      *
-     * @param f the real array to be scaled
-     * @param d the real scaling coefficient
-     * @return a reference to the scaled array
+     * @param f Array to be scaled.
+     * @param d Scaling coefficient.
+     * @return a reference to the scaled array.
      */
-    public static double[] scaleArray(double[] f, double d) {
-
+    static double[] scaleInPlace(double[] f, double d) {
         for (int i = 0; i < f.length; i++) {
             f[i] *= d;
         }
@@ -68,12 +48,11 @@ public class TransformUtils {
      * Multiply every component in the given complex array by the
      * given real number. The change is made in place.
      *
-     * @param f the complex array to be scaled
-     * @param d the real scaling coefficient
-     * @return a reference to the scaled array
+     * @param f Array to be scaled.
+     * @param d Scaling coefficient.
+     * @return the scaled array.
      */
-    public static Complex[] scaleArray(Complex[] f, double d) {
-
+    static Complex[] scaleInPlace(Complex[] f, double d) {
         for (int i = 0; i < f.length; i++) {
             f[i] = Complex.ofCartesian(d * f[i].getReal(), d * f[i].getImaginary());
         }
@@ -90,11 +69,11 @@ public class TransformUtils {
      * <li>{@code dataRI[1][i] = dataC[i].getImaginary()}.</li>
      * </ul>
      *
-     * @param dataC the array of {@link Complex} data to be transformed
+     * @param dataC Array of {@link Complex} data to be transformed.
      * @return a two dimensional array filled with the real and imaginary parts
-     *   of the specified complex input
+     * of the specified complex input.
      */
-    public static double[][] createRealImaginaryArray(final Complex[] dataC) {
+    static double[][] createRealImaginary(final Complex[] dataC) {
         final double[][] dataRI = new double[2][dataC.length];
         final double[] dataR = dataRI[0];
         final double[] dataI = dataRI[1];
@@ -115,21 +94,21 @@ public class TransformUtils {
      * <li>{@code dataC[i].getImaginary() = dataRI[1][i]}.</li>
      * </ul>
      *
-     * @param dataRI the array of real and imaginary parts to be transformed
-     * @return an array of {@link Complex} with specified real and imaginary parts.
-     * @throws DimensionMismatchException if the number of rows of the specified
-     *   array is not two, or the array is not rectangular
+     * @param dataRI Array of real and imaginary parts to be transformed.
+     * @return a {@link Complex} array.
+     * @throws IllegalArgumentException if the number of rows of the specified
+     * array is not two, or the array is not rectangular.
      */
-    public static Complex[] createComplexArray(final double[][] dataRI)
-        throws DimensionMismatchException{
-
+    static Complex[] createComplex(final double[][] dataRI) {
         if (dataRI.length != 2) {
-            throw new DimensionMismatchException(dataRI.length, 2);
+            throw new TransformException(TransformException.SIZE_MISMATCH,
+                                         dataRI.length, 2);
         }
         final double[] dataR = dataRI[0];
         final double[] dataI = dataRI[1];
         if (dataR.length != dataI.length) {
-            throw new DimensionMismatchException(dataI.length, dataR.length);
+            throw new TransformException(TransformException.SIZE_MISMATCH,
+                                         dataI.length, dataR.length);
         }
 
         final int n = dataR.length;
@@ -140,24 +119,39 @@ public class TransformUtils {
         return c;
     }
 
-
     /**
-     * Returns the base-2 logarithm of the specified {@code int}. Throws an
-     * exception if {@code n} is not a power of two.
+     * Samples the specified univariate real function on the specified interval.
+     * <p>
+     * The interval is divided equally into {@code n} sections and sample points
+     * are taken from {@code min} to {@code max - (max - min) / n}; therefore
+     * {@code f} is not sampled at the upper bound {@code max}.</p>
      *
-     * @param n the {@code int} whose base-2 logarithm is to be evaluated
-     * @return the base-2 logarithm of {@code n}
-     * @throws MathIllegalArgumentException if {@code n} is not a power of two
+     * @param f Function to be sampled
+     * @param min Lower bound of the interval (included).
+     * @param max Upper bound of the interval (excluded).
+     * @param n Number of sample points.
+     * @return the array of samples.
+     * @throws IllegalArgumentException if the lower bound {@code min} is
+     * greater than, or equal to the upper bound {@code max}, if the number
+     * of sample points {@code n} is negative.
      */
-    public static int exactLog2(final int n)
-        throws MathIllegalArgumentException {
+    static double[] sample(DoubleUnaryOperator f,
+                           double min,
+                           double max,
+                           int n) {
+        if (n <= 0) {
+            throw new TransformException(TransformException.NOT_STRICTLY_POSITIVE,
+                                         Integer.valueOf(n));
+        }
+        if (min >= max) {
+            throw new TransformException(TransformException.TOO_LARGE, min, max);
+        }
 
-        int index = Arrays.binarySearch(TransformUtils.POWERS_OF_TWO, n);
-        if (index < 0) {
-            throw new MathIllegalArgumentException(
-                    LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING,
-                    Integer.valueOf(n));
+        final double[] s = new double[n];
+        final double h = (max - min) / n;
+        for (int i = 0; i < n; i++) {
+            s[i] = f.applyAsDouble(min + i * h);
         }
-        return index;
+        return s;
     }
 }
diff --git a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/package-info.java b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/package-info.java
similarity index 85%
rename from commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/package-info.java
rename to commons-math-transform/src/main/java/org/apache/commons/math4/transform/package-info.java
index dfe1eed..5febfd4 100644
--- a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/transform/package-info.java
+++ b/commons-math-transform/src/main/java/org/apache/commons/math4/transform/package-info.java
@@ -14,9 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 /**
- *
- *     Implementations of transform methods, including Fast Fourier transforms.
- *
+ * Implementations of transform methods.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastCosineTransformerTest.java b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastCosineTransformerTest.java
similarity index 57%
rename from commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastCosineTransformerTest.java
rename to commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastCosineTransformerTest.java
index 0543f6f..41212a4 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastCosineTransformerTest.java
+++ b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastCosineTransformerTest.java
@@ -14,29 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.function.DoubleUnaryOperator;
 
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.analysis.function.Sin;
-import org.apache.commons.math4.legacy.analysis.function.Sinc;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
-import org.apache.commons.math4.legacy.util.FastMath;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Sin;
+import org.apache.commons.math3.analysis.function.Sinc;
+
 /**
- * Test case for fast cosine transformer.
+ * Test case for {@link FastCosineTransform}.
  * <p>
  * FCT algorithm is exact, the small tolerance number is used only to account
  * for round-off errors.
- *
  */
 @RunWith(value = Parameterized.class)
 public final class FastCosineTransformerTest
@@ -59,13 +57,13 @@ public final class FastCosineTransformerTest
             128
         };
         this.relativeTolerance = new double[] {
-            1E-15, 1E-15, 1E-14, 1E-13, 1E-13, 1E-12, 1E-11, 1E-10
+            1e-15, 1e-15, 1e-14, 1e-13, 1e-13, 1e-12, 1e-11, 1e-10
         };
     }
 
     /**
-     * Returns an array containing {@code true, false} in order to check both
-     * standard and orthogonal DCTs.
+     * Returns an array containing {@code true, false} in order to
+     * check both standard and orthogonal DCTs.
      *
      * @return an array of parameters for this parameterized test
      */
@@ -80,8 +78,8 @@ public final class FastCosineTransformerTest
     }
 
     @Override
-    RealTransformer createRealTransformer() {
-        return new FastCosineTransformer(normalization);
+    RealTransform createRealTransformer(boolean inverse) {
+        return new FastCosineTransform(normalization, inverse);
     }
 
     @Override
@@ -110,8 +108,9 @@ public final class FastCosineTransformerTest
     }
 
     @Override
-    UnivariateFunction getValidFunction() {
-        return new Sinc();
+    DoubleUnaryOperator getValidFunction() {
+        final UnivariateFunction sinc = new Sinc();
+        return (x) -> sinc.value(x);
     }
 
     @Override
@@ -121,16 +120,17 @@ public final class FastCosineTransformerTest
 
     @Override
     double getValidUpperBound() {
-        return FastMath.PI;
+        return Math.PI;
     }
 
     @Override
-    double[] transform(final double[] x, final TransformType type) {
+    double[] transform(final double[] x,
+                       final boolean inverse) {
         final int n = x.length;
         final double[] y = new double[n];
         final double[] cos = new double[2 * (n - 1)];
         for (int i = 0; i < cos.length; i++) {
-            cos[i] = FastMath.cos(FastMath.PI * i / (n - 1.0));
+            cos[i] = Math.cos(Math.PI * i / (n - 1.0));
         }
         int sgn = 1;
         for (int j = 0; j < n; j++) {
@@ -142,73 +142,66 @@ public final class FastCosineTransformerTest
             sgn *= -1;
         }
         final double s;
-        if (type == TransformType.FORWARD) {
+        if (!inverse) {
             if (normalization == DctNormalization.STANDARD_DCT_I) {
                 s = 1.0;
             } else if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
-                s = FastMath.sqrt(2.0 / (n - 1.0));
+                s = Math.sqrt(2.0 / (n - 1.0));
             } else {
-                throw new MathIllegalStateException();
+                throw new IllegalStateException();
             }
-        } else if (type == TransformType.INVERSE) {
+        } else {
             if (normalization == DctNormalization.STANDARD_DCT_I) {
                 s = 2.0 / (n - 1.0);
             } else if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
-                s = FastMath.sqrt(2.0 / (n - 1.0));
+                s = Math.sqrt(2.0 / (n - 1.0));
             } else {
-                throw new MathIllegalStateException();
+                throw new IllegalStateException();
             }
-        } else {
-            /*
-             * Should never occur. This clause is a safeguard in case other
-             * types are used to TransformType (which should not be done).
-             */
-            throw new MathIllegalStateException();
         }
-        TransformUtils.scaleArray(y, s);
+        TransformUtils.scaleInPlace(y, s);
         return y;
     }
 
-    /*
-     * Additional tests.
-     */
+    // Additional tests.
 
     /** Test of transformer for the ad hoc data. */
     @Test
     public void testAdHocData() {
-        FastCosineTransformer transformer;
-        transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
-        double result[], tolerance = 1E-12;
+        FastCosineTransform transformer;
+        double result[], tolerance = 1e-12;
 
-        double x[] = {
+        final double x[] = {
             0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0
         };
-        double y[] =
-            {
-                172.0, -105.096569476353, 27.3137084989848, -12.9593152353742,
-                8.0, -5.78585076868676, 4.68629150101524, -4.15826451958632,
-                4.0
-            };
-
-        result = transformer.transform(x, TransformType.FORWARD);
+        final double y[] = {
+            172.0, -105.096569476353, 27.3137084989848, -12.9593152353742,
+            8.0, -5.78585076868676, 4.68629150101524, -4.15826451958632,
+            4.0
+        };
+
+        transformer = new FastCosineTransform(DctNormalization.STANDARD_DCT_I);
+        result = transformer.apply(x);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(y[i], result[i], tolerance);
         }
 
-        result = transformer.transform(y, TransformType.INVERSE);
+        transformer = new FastCosineTransform(DctNormalization.STANDARD_DCT_I, true);
+        result = transformer.apply(y);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(x[i], result[i], tolerance);
         }
 
-        TransformUtils.scaleArray(x, FastMath.sqrt(0.5 * (x.length - 1)));
+        TransformUtils.scaleInPlace(x, Math.sqrt(0.5 * (x.length - 1)));
 
-        transformer = new FastCosineTransformer(DctNormalization.ORTHOGONAL_DCT_I);
-        result = transformer.transform(y, TransformType.FORWARD);
+        transformer = new FastCosineTransform(DctNormalization.ORTHOGONAL_DCT_I);
+        result = transformer.apply(y);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(x[i], result[i], tolerance);
         }
 
-        result = transformer.transform(x, TransformType.INVERSE);
+        transformer = new FastCosineTransform(DctNormalization.ORTHOGONAL_DCT_I, true);
+        result = transformer.apply(x);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(y[i], result[i], tolerance);
         }
@@ -216,31 +209,30 @@ public final class FastCosineTransformerTest
 
     /** Test of parameters for the transformer. */
     @Test
-    public void testParameters()
-        throws Exception {
-        UnivariateFunction f = new Sin();
-        FastCosineTransformer transformer;
-        transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
+    public void testParameters() throws Exception {
+        final UnivariateFunction sinFunction = new Sin();
+        final DoubleUnaryOperator f = (x) -> sinFunction.value(x);
+        final FastCosineTransform transformer = new FastCosineTransform(DctNormalization.STANDARD_DCT_I);
 
         try {
             // bad interval
-            transformer.transform(f, 1, -1, 65, TransformType.FORWARD);
-            Assert.fail("Expecting MathIllegalArgumentException - bad interval");
-        } catch (MathIllegalArgumentException ex) {
+            transformer.apply(f, 1, -1, 65);
+            Assert.fail("Expecting IllegalArgumentException - bad interval");
+        } catch (IllegalArgumentException ex) {
             // expected
         }
         try {
             // bad samples number
-            transformer.transform(f, -1, 1, 1, TransformType.FORWARD);
-            Assert.fail("Expecting MathIllegalArgumentException - bad samples number");
-        } catch (MathIllegalArgumentException ex) {
+            transformer.apply(f, -1, 1, 1);
+            Assert.fail("Expecting IllegalArgumentException - bad samples number");
+        } catch (IllegalArgumentException ex) {
             // expected
         }
         try {
             // bad samples number
-            transformer.transform(f, -1, 1, 64, TransformType.FORWARD);
-            Assert.fail("Expecting MathIllegalArgumentException - bad samples number");
-        } catch (MathIllegalArgumentException ex) {
+            transformer.apply(f, -1, 1, 64);
+            Assert.fail("Expecting IllegalArgumentException - bad samples number");
+        } catch (IllegalArgumentException ex) {
             // expected
         }
     }
@@ -248,27 +240,26 @@ public final class FastCosineTransformerTest
     /** Test of transformer for the sine function. */
     @Test
     public void testSinFunction() {
-        UnivariateFunction f = new Sin();
-        FastCosineTransformer transformer;
-        transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
-        double min, max, result[], tolerance = 1E-12;
+        final UnivariateFunction sinFunction = new Sin();
+        final DoubleUnaryOperator f = (x) -> sinFunction.value(x);
+        final FastCosineTransform transformer = new FastCosineTransform(DctNormalization.STANDARD_DCT_I);
+        double min, max, result[], tolerance = 1e-12;
         int N = 9;
 
-        double expected[] =
-            {
-                0.0, 3.26197262739567, 0.0, -2.17958042710327, 0.0,
-                -0.648846697642915, 0.0, -0.433545502649478, 0.0
-            };
+        final double expected[] = {
+            0.0, 3.26197262739567, 0.0, -2.17958042710327, 0.0,
+            -0.648846697642915, 0.0, -0.433545502649478, 0.0
+        };
         min = 0.0;
-        max = 2.0 * FastMath.PI * N / (N - 1);
-        result = transformer.transform(f, min, max, N, TransformType.FORWARD);
+        max = 2.0 * Math.PI * N / (N - 1);
+        result = transformer.apply(f, min, max, N);
         for (int i = 0; i < N; i++) {
             Assert.assertEquals(expected[i], result[i], tolerance);
         }
 
-        min = -FastMath.PI;
-        max = FastMath.PI * (N + 1) / (N - 1);
-        result = transformer.transform(f, min, max, N, TransformType.FORWARD);
+        min = -Math.PI;
+        max = Math.PI * (N + 1) / (N - 1);
+        result = transformer.apply(f, min, max, N);
         for (int i = 0; i < N; i++) {
             Assert.assertEquals(-expected[i], result[i], tolerance);
         }
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastFourierTransformerTest.java b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastFourierTransformerTest.java
similarity index 53%
rename from commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastFourierTransformerTest.java
rename to commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastFourierTransformerTest.java
index 3b964bd..4653d02 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastFourierTransformerTest.java
+++ b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastFourierTransformerTest.java
@@ -14,35 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
-import java.util.Random;
+import java.util.function.DoubleUnaryOperator;
 
-import org.apache.commons.numbers.complex.Complex;
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.analysis.function.Sin;
-import org.apache.commons.math4.legacy.analysis.function.Sinc;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
-import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
-import org.apache.commons.math4.legacy.util.FastMath;
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.apache.commons.numbers.complex.Complex;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Sin;
+import org.apache.commons.math3.analysis.function.Sinc;
+
 /**
- * Test case for fast Fourier transformer.
+ * Test case for {@link FastFourierTransform}.
  * <p>
  * FFT algorithm is exact, the small tolerance number is used only
  * to account for round-off errors.
- *
  */
 public final class FastFourierTransformerTest {
-    /** The common seed of all random number generators used in this test. */
-    private final static long SEED = 20110111L;
+    private static final Sin SIN_FUNCTION = new Sin();
+    private static final DoubleUnaryOperator SIN = (x) -> SIN_FUNCTION.value(x);
 
-    /*
-     * Precondition checks.
-     */
+    /** RNG. */
+    private static final UniformRandomProvider RNG = RandomSource.create(RandomSource.MWC_256);
+
+    // Precondition checks.
 
     @Test
     public void testTransformComplexSizeNotAPowerOfTwo() {
@@ -50,17 +50,14 @@ public final class FastFourierTransformerTest {
         final Complex[] x = createComplexData(n);
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                final FastFourierTransformer fft;
-                fft = new FastFourierTransformer(norm[i]);
+            for (boolean type : new boolean[] { true, false }) {
+                final FastFourierTransform fft = new FastFourierTransform(norm[i], type);
                 try {
-                    fft.transform(x, type[j]);
-                    Assert.fail(norm[i] + ", " + type[j] +
-                        ": MathIllegalArgumentException was expected");
-                } catch (MathIllegalArgumentException e) {
+                    fft.apply(x);
+                    Assert.fail(norm[i] + ", " + type +
+                        ": IllegalArgumentException was expected");
+                } catch (IllegalArgumentException e) {
                     // Expected behaviour
                 }
             }
@@ -73,17 +70,14 @@ public final class FastFourierTransformerTest {
         final double[] x = createRealData(n);
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                final FastFourierTransformer fft;
-                fft = new FastFourierTransformer(norm[i]);
+            for (boolean type : new boolean[] { true, false }) {
+                final FastFourierTransform fft = new FastFourierTransform(norm[i], type);
                 try {
-                    fft.transform(x, type[j]);
-                    Assert.fail(norm[i] + ", " + type[j] +
-                        ": MathIllegalArgumentException was expected");
-                } catch (MathIllegalArgumentException e) {
+                    fft.apply(x);
+                    Assert.fail(norm[i] + ", " + type +
+                        ": IllegalArgumentException was expected");
+                } catch (IllegalArgumentException e) {
                     // Expected behaviour
                 }
             }
@@ -93,20 +87,16 @@ public final class FastFourierTransformerTest {
     @Test
     public void testTransformFunctionSizeNotAPowerOfTwo() {
         final int n = 127;
-        final UnivariateFunction f = new Sin();
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                final FastFourierTransformer fft;
-                fft = new FastFourierTransformer(norm[i]);
+            for (boolean type : new boolean[] { true, false }) {
+                final FastFourierTransform fft = new FastFourierTransform(norm[i], type);
                 try {
-                    fft.transform(f, 0.0, Math.PI, n, type[j]);
-                    Assert.fail(norm[i] + ", " + type[j] +
-                        ": MathIllegalArgumentException was expected");
-                } catch (MathIllegalArgumentException e) {
+                    fft.apply(SIN, 0.0, Math.PI, n);
+                    Assert.fail(norm[i] + ", " + type +
+                        ": IllegalArgumentException was expected");
+                } catch (IllegalArgumentException e) {
                     // Expected behaviour
                 }
             }
@@ -116,21 +106,17 @@ public final class FastFourierTransformerTest {
     @Test
     public void testTransformFunctionNotStrictlyPositiveNumberOfSamples() {
         final int n = -128;
-        final UnivariateFunction f = new Sin();
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                final FastFourierTransformer fft;
-                fft = new FastFourierTransformer(norm[i]);
+            for (boolean type : new boolean[] { true, false }) {
+                final FastFourierTransform fft = new FastFourierTransform(norm[i], type);
                 try {
-                    fft.transform(f, 0.0, Math.PI, n, type[j]);
-                    fft.transform(f, 0.0, Math.PI, n, type[j]);
-                    Assert.fail(norm[i] + ", " + type[j] +
-                        ": NotStrictlyPositiveException was expected");
-                } catch (NotStrictlyPositiveException e) {
+                    fft.apply(SIN, 0.0, Math.PI, n);
+                    fft.apply(SIN, 0.0, Math.PI, n);
+                    Assert.fail(norm[i] + ", " + type +
+                        ": IllegalArgumentException was expected");
+                } catch (IllegalArgumentException e) {
                     // Expected behaviour
                 }
             }
@@ -140,46 +126,38 @@ public final class FastFourierTransformerTest {
     @Test
     public void testTransformFunctionInvalidBounds() {
         final int n = 128;
-        final UnivariateFunction f = new Sin();
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                final FastFourierTransformer fft;
-                fft = new FastFourierTransformer(norm[i]);
+            for (boolean type : new boolean[] { true, false }) {
+                final FastFourierTransform fft = new FastFourierTransform(norm[i], type);
                 try {
-                    fft.transform(f, Math.PI, 0.0, n, type[j]);
-                    Assert.fail(norm[i] + ", " + type[j] +
-                        ": NumberIsTooLargeException was expected");
-                } catch (NumberIsTooLargeException e) {
+                    fft.apply(SIN, Math.PI, 0.0, n);
+                    Assert.fail(norm[i] + ", " + type +
+                        ": IllegalArgumentException was expected");
+                } catch (IllegalArgumentException e) {
                     // Expected behaviour
                 }
             }
         }
     }
 
-    /*
-     * Utility methods for checking (successful) transforms.
-     */
+    // Utility methods for checking (successful) transforms.
 
     private static Complex[] createComplexData(final int n) {
-        final Random random = new Random(SEED);
         final Complex[] data = new Complex[n];
         for (int i = 0; i < n; i++) {
-            final double re = 2.0 * random.nextDouble() - 1.0;
-            final double im = 2.0 * random.nextDouble() - 1.0;
+            final double re = 2 * RNG.nextDouble() - 1;
+            final double im = 2 * RNG.nextDouble() - 1;
             data[i] = Complex.ofCartesian(re, im);
         }
         return data;
     }
 
     private static double[] createRealData(final int n) {
-        final Random random = new Random(SEED);
         final double[] data = new double[n];
         for (int i = 0; i < n; i++) {
-            data[i] = 2.0 * random.nextDouble() - 1.0;
+            data[i] = 2 * RNG.nextDouble() - 1;
         }
         return data;
     }
@@ -191,9 +169,9 @@ public final class FastFourierTransformerTest {
         final double[] sin = new double[n];
         final Complex[] y = new Complex[n];
         for (int i = 0; i < n; i++) {
-            final double arg = 2.0 * FastMath.PI * i / n;
-            cos[i] = FastMath.cos(arg);
-            sin[i] = FastMath.sin(arg);
+            final double arg = 2.0 * Math.PI * i / n;
+            cos[i] = Math.cos(arg);
+            sin[i] = Math.sin(arg);
         }
         for (int i = 0; i < n; i++) {
             double yr = 0.0;
@@ -212,47 +190,47 @@ public final class FastFourierTransformerTest {
         return y;
     }
 
-    private static void doTestTransformComplex(final int n, final double tol,
-        final DftNormalization normalization,
-        final TransformType type) {
-        final FastFourierTransformer fft;
-        fft = new FastFourierTransformer(normalization);
+    private static void doTestTransformComplex(final int n,
+                                               final double tol,
+                                               final DftNormalization normalization,
+                                               boolean inverse) {
+        final FastFourierTransform fft = new FastFourierTransform(normalization, inverse);
         final Complex[] x = createComplexData(n);
         final Complex[] expected;
         final double s;
-        if (type==TransformType.FORWARD) {
+        if (!inverse) {
             expected = dft(x, -1);
             if (normalization == DftNormalization.STANDARD){
                 s = 1.0;
             } else {
-                s = 1.0 / FastMath.sqrt(n);
+                s = 1.0 / Math.sqrt(n);
             }
         } else {
             expected = dft(x, 1);
             if (normalization == DftNormalization.STANDARD) {
                 s = 1.0 / n;
             } else {
-                s = 1.0 / FastMath.sqrt(n);
+                s = 1.0 / Math.sqrt(n);
             }
         }
-        final Complex[] actual = fft.transform(x, type);
+        final Complex[] actual = fft.apply(x);
         for (int i = 0; i < n; i++) {
             final String msg;
-            msg = String.format("%s, %s, %d, %d", normalization, type, n, i);
+            msg = String.format("%s, %s, %d, %d", normalization, inverse, n, i);
             final double re = s * expected[i].getReal();
             Assert.assertEquals(msg, re, actual[i].getReal(),
-                tol * FastMath.abs(re));
+                                tol * Math.abs(re));
             final double im = s * expected[i].getImaginary();
-            Assert.assertEquals(msg, im, actual[i].getImaginary(), tol *
-                FastMath.abs(re));
+            Assert.assertEquals(msg, im, actual[i].getImaginary(),
+                                tol * Math.abs(re));
         }
     }
 
-    private static void doTestTransformReal(final int n, final double tol,
-        final DftNormalization normalization,
-        final TransformType type) {
-        final FastFourierTransformer fft;
-        fft = new FastFourierTransformer(normalization);
+    private static void doTestTransformReal(final int n,
+                                            final double tol,
+                                            final DftNormalization normalization,
+                                            final boolean inverse) {
+        final FastFourierTransform fft = new FastFourierTransform(normalization, inverse);
         final double[] x = createRealData(n);
         final Complex[] xc = new Complex[n];
         for (int i = 0; i < n; i++) {
@@ -260,93 +238,91 @@ public final class FastFourierTransformerTest {
         }
         final Complex[] expected;
         final double s;
-        if (type == TransformType.FORWARD) {
+        if (!inverse) {
             expected = dft(xc, -1);
             if (normalization == DftNormalization.STANDARD) {
                 s = 1.0;
             } else {
-                s = 1.0 / FastMath.sqrt(n);
+                s = 1.0 / Math.sqrt(n);
             }
         } else {
             expected = dft(xc, 1);
             if (normalization == DftNormalization.STANDARD) {
                 s = 1.0 / n;
             } else {
-                s = 1.0 / FastMath.sqrt(n);
+                s = 1.0 / Math.sqrt(n);
             }
         }
-        final Complex[] actual = fft.transform(x, type);
+        final Complex[] actual = fft.apply(x);
         for (int i = 0; i < n; i++) {
             final String msg;
-            msg = String.format("%s, %s, %d, %d", normalization, type, n, i);
+            msg = String.format("%s, %s, %d, %d", normalization, inverse, n, i);
             final double re = s * expected[i].getReal();
             Assert.assertEquals(msg, re, actual[i].getReal(),
-                tol * FastMath.abs(re));
+                                tol * Math.abs(re));
             final double im = s * expected[i].getImaginary();
-            Assert.assertEquals(msg, im, actual[i].getImaginary(), tol *
-                FastMath.abs(re));
+            Assert.assertEquals(msg, im, actual[i].getImaginary(),
+                                tol * Math.abs(re));
         }
     }
 
-    private static void doTestTransformFunction(final UnivariateFunction f,
-        final double min, final double max, int n, final double tol,
-        final DftNormalization normalization,
-        final TransformType type) {
-        final FastFourierTransformer fft;
-        fft = new FastFourierTransformer(normalization);
+    private static void doTestTransformFunction(final DoubleUnaryOperator f,
+                                                final double min,
+                                                final double max,
+                                                int n,
+                                                final double tol,
+                                                final DftNormalization normalization,
+                                                final boolean inverse) {
+        final FastFourierTransform fft = new FastFourierTransform(normalization, inverse);
         final Complex[] x = new Complex[n];
         for (int i = 0; i < n; i++) {
             final double t = min + i * (max - min) / n;
-            x[i] = Complex.ofCartesian(f.value(t), 0);
+            x[i] = Complex.ofCartesian(f.applyAsDouble(t), 0);
         }
         final Complex[] expected;
         final double s;
-        if (type == TransformType.FORWARD) {
+        if (!inverse) {
             expected = dft(x, -1);
             if (normalization == DftNormalization.STANDARD) {
                 s = 1.0;
             } else {
-                s = 1.0 / FastMath.sqrt(n);
+                s = 1.0 / Math.sqrt(n);
             }
         } else {
             expected = dft(x, 1);
             if (normalization == DftNormalization.STANDARD) {
                 s = 1.0 / n;
             } else {
-                s = 1.0 / FastMath.sqrt(n);
+                s = 1.0 / Math.sqrt(n);
             }
         }
-        final Complex[] actual = fft.transform(f, min, max, n, type);
+        final Complex[] actual = fft.apply(f, min, max, n);
         for (int i = 0; i < n; i++) {
             final String msg = String.format("%d, %d", n, i);
             final double re = s * expected[i].getReal();
             Assert.assertEquals(msg, re, actual[i].getReal(),
-                tol * FastMath.abs(re));
+                                tol * Math.abs(re));
             final double im = s * expected[i].getImaginary();
-            Assert.assertEquals(msg, im, actual[i].getImaginary(), tol *
-                FastMath.abs(re));
+            Assert.assertEquals(msg, im, actual[i].getImaginary(),
+                                tol * Math.abs(re));
         }
     }
 
-    /*
-     * Tests of standard transform (when data is valid).
-     */
+    // Tests of standard transform (when data is valid).
 
     @Test
     public void testTransformComplex() {
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                doTestTransformComplex(2, 1.0E-15, norm[i], type[j]);
-                doTestTransformComplex(4, 1.0E-14, norm[i], type[j]);
-                doTestTransformComplex(8, 1.0E-14, norm[i], type[j]);
-                doTestTransformComplex(16, 1.0E-13, norm[i], type[j]);
-                doTestTransformComplex(32, 1.0E-13, norm[i], type[j]);
-                doTestTransformComplex(64, 1.0E-12, norm[i], type[j]);
-                doTestTransformComplex(128, 1.0E-12, norm[i], type[j]);
+            for (boolean type : new boolean[] { true, false }) {
+                doTestTransformComplex(2, 1e-15, norm[i], type);
+                doTestTransformComplex(4, 1e-14, norm[i], type);
+                doTestTransformComplex(8, 1e-13, norm[i], type);
+                doTestTransformComplex(16, 1e-13, norm[i], type);
+                doTestTransformComplex(32, 1e-13, norm[i], type);
+                doTestTransformComplex(64, 1e-12, norm[i], type);
+                doTestTransformComplex(128, 1e-11, norm[i], type);
             }
         }
     }
@@ -355,58 +331,54 @@ public final class FastFourierTransformerTest {
     public void testStandardTransformReal() {
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                doTestTransformReal(2, 1.0E-15, norm[i], type[j]);
-                doTestTransformReal(4, 1.0E-14, norm[i], type[j]);
-                doTestTransformReal(8, 1.0E-14, norm[i], type[j]);
-                doTestTransformReal(16, 1.0E-13, norm[i], type[j]);
-                doTestTransformReal(32, 1.0E-13, norm[i], type[j]);
-                doTestTransformReal(64, 1.0E-13, norm[i], type[j]);
-                doTestTransformReal(128, 1.0E-11, norm[i], type[j]);
+            for (boolean type : new boolean[] { true, false }) {
+                doTestTransformReal(2, 1e-15, norm[i], type);
+                doTestTransformReal(4, 1e-14, norm[i], type);
+                doTestTransformReal(8, 1e-13, norm[i], type);
+                doTestTransformReal(16, 1e-13, norm[i], type);
+                doTestTransformReal(32, 1e-12, norm[i], type);
+                doTestTransformReal(64, 1e-12, norm[i], type);
+                doTestTransformReal(128, 1e-11, norm[i], type);
             }
         }
     }
 
     @Test
     public void testStandardTransformFunction() {
-        final UnivariateFunction f = new Sinc();
-        final double min = -FastMath.PI;
-        final double max = FastMath.PI;
+        final UnivariateFunction sinc = new Sinc();
+        final DoubleUnaryOperator f = (x) -> sinc.value(x);
+
+        final double min = -Math.PI;
+        final double max = Math.PI;
         final DftNormalization[] norm;
         norm = DftNormalization.values();
-        final TransformType[] type;
-        type = TransformType.values();
+
         for (int i = 0; i < norm.length; i++) {
-            for (int j = 0; j < type.length; j++) {
-                doTestTransformFunction(f, min, max, 2, 1.0E-15, norm[i], type[j]);
-                doTestTransformFunction(f, min, max, 4, 1.0E-14, norm[i], type[j]);
-                doTestTransformFunction(f, min, max, 8, 1.0E-14, norm[i], type[j]);
-                doTestTransformFunction(f, min, max, 16, 1.0E-13, norm[i], type[j]);
-                doTestTransformFunction(f, min, max, 32, 1.0E-13, norm[i], type[j]);
-                doTestTransformFunction(f, min, max, 64, 1.0E-12, norm[i], type[j]);
-                doTestTransformFunction(f, min, max, 128, 1.0E-11, norm[i], type[j]);
+            for (boolean type : new boolean[] { true, false }) {
+                doTestTransformFunction(f, min, max, 2, 1e-15, norm[i], type);
+                doTestTransformFunction(f, min, max, 4, 1e-14, norm[i], type);
+                doTestTransformFunction(f, min, max, 8, 1e-14, norm[i], type);
+                doTestTransformFunction(f, min, max, 16, 1e-13, norm[i], type);
+                doTestTransformFunction(f, min, max, 32, 1e-13, norm[i], type);
+                doTestTransformFunction(f, min, max, 64, 1e-12, norm[i], type);
+                doTestTransformFunction(f, min, max, 128, 1e-11, norm[i], type);
             }
         }
     }
 
-    /*
-     * Additional tests for 1D data.
-     */
+    // Additional tests for 1D data.
 
     /**
      * Test of transformer for the ad hoc data taken from Mathematica.
      */
     @Test
     public void testAdHocData() {
-        FastFourierTransformer transformer;
-        transformer = new FastFourierTransformer(DftNormalization.STANDARD);
-        Complex result[]; double tolerance = 1E-12;
+        FastFourierTransform transformer;
+        Complex result[]; double tolerance = 1e-12;
 
-        double x[] = {1.3, 2.4, 1.7, 4.1, 2.9, 1.7, 5.1, 2.7};
-        Complex y[] = {
+        final double x[] = {1.3, 2.4, 1.7, 4.1, 2.9, 1.7, 5.1, 2.7};
+        final Complex y[] = {
             Complex.ofCartesian(21.9, 0.0),
             Complex.ofCartesian(-2.09497474683058, 1.91507575950825),
             Complex.ofCartesian(-2.6, 2.7),
@@ -416,30 +388,33 @@ public final class FastFourierTransformerTest {
             Complex.ofCartesian(-2.6, -2.7),
             Complex.ofCartesian(-2.09497474683058, -1.91507575950825)};
 
-        result = transformer.transform(x, TransformType.FORWARD);
+        transformer = new FastFourierTransform(DftNormalization.STANDARD);
+        result = transformer.apply(x);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(y[i].getReal(), result[i].getReal(), tolerance);
             Assert.assertEquals(y[i].getImaginary(), result[i].getImaginary(), tolerance);
         }
 
-        result = transformer.transform(y, TransformType.INVERSE);
+        transformer = new FastFourierTransform(DftNormalization.STANDARD, true);
+        result = transformer.apply(y);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(x[i], result[i].getReal(), tolerance);
             Assert.assertEquals(0.0, result[i].getImaginary(), tolerance);
         }
 
         double x2[] = {10.4, 21.6, 40.8, 13.6, 23.2, 32.8, 13.6, 19.2};
-        TransformUtils.scaleArray(x2, 1.0 / FastMath.sqrt(x2.length));
+        TransformUtils.scaleInPlace(x2, 1.0 / Math.sqrt(x2.length));
         Complex y2[] = y;
 
-        transformer = new FastFourierTransformer(DftNormalization.UNITARY);
-        result = transformer.transform(y2, TransformType.FORWARD);
+        transformer = new FastFourierTransform(DftNormalization.UNITARY);
+        result = transformer.apply(y2);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(x2[i], result[i].getReal(), tolerance);
             Assert.assertEquals(0.0, result[i].getImaginary(), tolerance);
         }
 
-        result = transformer.transform(x2, TransformType.INVERSE);
+        transformer = new FastFourierTransform(DftNormalization.UNITARY, true);
+        result = transformer.apply(x2);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(y2[i].getReal(), result[i].getReal(), tolerance);
             Assert.assertEquals(y2[i].getImaginary(), result[i].getImaginary(), tolerance);
@@ -451,14 +426,13 @@ public final class FastFourierTransformerTest {
      */
     @Test
     public void testSinFunction() {
-        UnivariateFunction f = new Sin();
-        FastFourierTransformer transformer;
-        transformer = new FastFourierTransformer(DftNormalization.STANDARD);
+        FastFourierTransform transformer;
         Complex result[]; int N = 1 << 8;
-        double min, max, tolerance = 1E-12;
+        double min, max, tolerance = 1e-12;
 
-        min = 0.0; max = 2.0 * FastMath.PI;
-        result = transformer.transform(f, min, max, N, TransformType.FORWARD);
+        min = 0.0; max = 2.0 * Math.PI;
+        transformer = new FastFourierTransform(DftNormalization.STANDARD);
+        result = transformer.apply(SIN, min, max, N);
         Assert.assertEquals(0.0, result[1].getReal(), tolerance);
         Assert.assertEquals(-(N >> 1), result[1].getImaginary(), tolerance);
         Assert.assertEquals(0.0, result[N-1].getReal(), tolerance);
@@ -468,8 +442,9 @@ public final class FastFourierTransformerTest {
             Assert.assertEquals(0.0, result[i].getImaginary(), tolerance);
         }
 
-        min = -FastMath.PI; max = FastMath.PI;
-        result = transformer.transform(f, min, max, N, TransformType.INVERSE);
+        min = -Math.PI; max = Math.PI;
+        transformer = new FastFourierTransform(DftNormalization.STANDARD, true);
+        result = transformer.apply(SIN, min, max, N);
         Assert.assertEquals(0.0, result[1].getReal(), tolerance);
         Assert.assertEquals(-0.5, result[1].getImaginary(), tolerance);
         Assert.assertEquals(0.0, result[N-1].getReal(), tolerance);
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastHadamardTransformerTest.java b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastHadamardTransformerTest.java
similarity index 70%
rename from commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastHadamardTransformerTest.java
rename to commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastHadamardTransformerTest.java
index fd783ee..3499317 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastHadamardTransformerTest.java
+++ b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastHadamardTransformerTest.java
@@ -14,27 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.commons.numbers.core.Precision;
+
 
 /**
- * JUnit Test for HadamardTransformerTest
- * @see org.apache.commons.math4.legacy.transform.FastHadamardTransformer
+ * Test for {@link FestHadamardTransform}.
  */
 public final class FastHadamardTransformerTest {
-
     /**
      * Test of transformer for the a 8-point FHT (means n=8)
      */
     @Test
     public void test8Points() {
         checkAllTransforms(new int[] { 1, 4, -2, 3, 0, 1, 4, -1 },
-                       new int[] { 10, -4, 2, -4, 2, -12, 6, 8 });
+                           new int[] { 10, -4, 2, -4, 2, -12, 6, 8 });
     }
 
     /**
@@ -51,8 +49,8 @@ public final class FastHadamardTransformerTest {
      */
     @Test
     public void testNoIntInverse() {
-        FastHadamardTransformer transformer = new FastHadamardTransformer();
-        double[] x = transformer.transform(new double[] { 0, 1, 0, 1}, TransformType.INVERSE);
+        final FastHadamardTransform transformer = new FastHadamardTransform(true);
+        final double[] x = transformer.apply(new double[] { 0, 1, 0, 1});
         Assert.assertEquals( 0.5, x[0], 0);
         Assert.assertEquals(-0.5, x[1], 0);
         Assert.assertEquals( 0.0, x[2], 0);
@@ -65,41 +63,41 @@ public final class FastHadamardTransformerTest {
     @Test
     public void test3Points() {
         try {
-            new FastHadamardTransformer().transform(new double[3], TransformType.FORWARD);
+            new FastHadamardTransform().apply(new double[3]);
             Assert.fail("an exception should have been thrown");
-        } catch (MathIllegalArgumentException iae) {
+        } catch (IllegalArgumentException iae) {
             // expected
         }
     }
 
-    private void checkAllTransforms(int[]x, int[] y) {
+    private void checkAllTransforms(int[] x, int[] y) {
         checkDoubleTransform(x, y);
         checkInverseDoubleTransform(x, y);
         checkIntTransform(x, y);
     }
 
-    private void checkDoubleTransform(int[]x, int[] y) {
+    private void checkDoubleTransform(int[] x, int[] y) {
         // Initiate the transformer
-        FastHadamardTransformer transformer = new FastHadamardTransformer();
+        final FastHadamardTransform transformer = new FastHadamardTransform();
 
         // check double transform
-        double[] dX = new double[x.length];
+        final double[] dX = new double[x.length];
         for (int i = 0; i < dX.length; ++i) {
             dX[i] = x[i];
         }
-        double dResult[] = transformer.transform(dX, TransformType.FORWARD);
+        final double dResult[] = transformer.apply(dX);
         for (int i = 0; i < dResult.length; i++) {
             // compare computed results to precomputed results
             Assert.assertTrue(Precision.equals(y[i], dResult[i], 1));
         }
     }
 
-    private void checkIntTransform(int[]x, int[] y) {
+    private void checkIntTransform(int[] x, int[] y) {
         // Initiate the transformer
-        FastHadamardTransformer transformer = new FastHadamardTransformer();
+        final FastHadamardTransform transformer = new FastHadamardTransform();
 
         // check integer transform
-        int iResult[] = transformer.transform(x);
+        final int iResult[] = transformer.apply(x);
         for (int i = 0; i < iResult.length; i++) {
             // compare computed results to precomputed results
             Assert.assertEquals(y[i], iResult[i]);
@@ -109,14 +107,14 @@ public final class FastHadamardTransformerTest {
 
     private void checkInverseDoubleTransform(int[]x, int[] y) {
         // Initiate the transformer
-        FastHadamardTransformer transformer = new FastHadamardTransformer();
+        final FastHadamardTransform transformer = new FastHadamardTransform(true);
 
         // check double transform
-        double[] dY = new double[y.length];
+        final double[] dY = new double[y.length];
         for (int i = 0; i < dY.length; ++i) {
             dY[i] = y[i];
         }
-        double dResult[] = transformer.transform(dY, TransformType.INVERSE);
+        final double dResult[] = transformer.apply(dY);
         for (int i = 0; i < dResult.length; i++) {
             // compare computed results to precomputed results
             Assert.assertTrue(Precision.equals(x[i], dResult[i], 1));
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastSineTransformerTest.java b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastSineTransformerTest.java
similarity index 58%
rename from commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastSineTransformerTest.java
rename to commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastSineTransformerTest.java
index 21df571..aeed09e 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/FastSineTransformerTest.java
+++ b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/FastSineTransformerTest.java
@@ -14,29 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.function.DoubleUnaryOperator;
 
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.analysis.function.Sin;
-import org.apache.commons.math4.legacy.analysis.function.Sinc;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
-import org.apache.commons.math4.legacy.util.FastMath;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Sin;
+import org.apache.commons.math3.analysis.function.Sinc;
+
 /**
- * Test case for fast sine transformer.
+ * Test case for {@link FastSineTransform}.
  * <p>
  * FST algorithm is exact, the small tolerance number is used only
  * to account for round-off errors.
- *
  */
 @RunWith(value = Parameterized.class)
 public final class FastSineTransformerTest extends RealTransformerAbstractTest {
@@ -58,7 +56,7 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
             129
         };
         this.relativeTolerance = new double[] {
-            1E-15, 1E-15, 1E-14, 1E-14, 1E-13, 1E-12, 1E-11, 1E-11
+            1e-15, 1e-15, 1e-14, 1e-14, 1e-13, 1e-12, 1e-11, 1e-11
         };
     }
 
@@ -66,7 +64,7 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
      * Returns an array containing {@code true, false} in order to check both
      * standard and orthogonal DSTs.
      *
-     * @return an array of parameters for this parameterized test
+     * @return an array of parameters for this parameterized test.
      */
     @Parameters
     public static Collection<Object[]> data() {
@@ -87,13 +85,13 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
     @Override
     double[] createRealData(final int n) {
         final double[] data = super.createRealData(n);
-        data[0] = 0.0;
+        data[0] = 0;
         return data;
     }
 
     @Override
-    RealTransformer createRealTransformer() {
-        return new FastSineTransformer(normalization);
+    RealTransform createRealTransformer(boolean inverse) {
+        return new FastSineTransform(normalization, inverse);
     }
 
     @Override
@@ -122,8 +120,9 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
     }
 
     @Override
-    UnivariateFunction getValidFunction() {
-        return new Sinc();
+    DoubleUnaryOperator getValidFunction() {
+        final UnivariateFunction sinc = new Sinc();
+        return (x) -> sinc.value(x);
     }
 
     @Override
@@ -133,16 +132,16 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
 
     @Override
     double getValidUpperBound() {
-        return FastMath.PI;
+        return Math.PI;
     }
 
     @Override
-    double[] transform(final double[] x, final TransformType type) {
+    double[] transform(final double[] x, boolean inverse) {
         final int n = x.length;
         final double[] y = new double[n];
         final double[] sin = new double[2 * n];
         for (int i = 0; i < sin.length; i++) {
-            sin[i] = FastMath.sin(FastMath.PI * i / n);
+            sin[i] = Math.sin(Math.PI * i / n);
         }
         for (int j = 0; j < n; j++) {
             double yj = 0.0;
@@ -152,90 +151,87 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
             y[j] = yj;
         }
         final double s;
-        if (type == TransformType.FORWARD) {
+        if (!inverse) {
             if (normalization == DstNormalization.STANDARD_DST_I) {
-                s = 1.0;
+                s = 1;
             } else if (normalization == DstNormalization.ORTHOGONAL_DST_I) {
-                s = FastMath.sqrt(2.0 / n);
+                s = Math.sqrt(2d / n);
             } else {
-                throw new MathIllegalStateException();
+                throw new IllegalStateException();
             }
-        } else if (type == TransformType.INVERSE) {
+        } else {
             if (normalization == DstNormalization.STANDARD_DST_I) {
-                s = 2.0 / n;
+                s = 2d / n;
             } else if (normalization == DstNormalization.ORTHOGONAL_DST_I) {
-                s = FastMath.sqrt(2.0 / n);
+                s = Math.sqrt(2d / n);
             } else {
-                throw new MathIllegalStateException();
+                throw new IllegalStateException();
             }
-        } else {
-            /*
-             * Should never occur. This clause is a safeguard in case other
-             * types are used to TransformType (which should not be done).
-             */
-            throw new MathIllegalStateException();
         }
-        TransformUtils.scaleArray(y, s);
+
+        TransformUtils.scaleInPlace(y, s);
         return y;
     }
 
-    /*
-     * Additional tests.
-     */
+    // Additional tests.
+
     @Test
     public void testTransformRealFirstElementNotZero() {
-        final TransformType[] type = TransformType.values();
         final double[] data = new double[] {
-            1.0, 1.0, 1.0, 1.0
+            1, 1, 1, 1
         };
-        final RealTransformer transformer = createRealTransformer();
-        for (int j = 0; j < type.length; j++) {
+        for (boolean type : new boolean[] { true, false }) {
             try {
-                transformer.transform(data, type[j]);
-                Assert.fail(type[j].toString());
-            } catch (MathIllegalArgumentException e) {
+                final RealTransform transformer = createRealTransformer(type);
+                transformer.apply(data);
+                Assert.fail("type=" + type);
+            } catch (IllegalArgumentException e) {
                 // Expected: do nothing
             }
         }
     }
 
-    /*
-     * Additional (legacy) tests.
-     */
+    // Additional (legacy) tests.
 
     /**
      * Test of transformer for the ad hoc data.
      */
     @Test
     public void testAdHocData() {
-        FastSineTransformer transformer;
-        transformer = new FastSineTransformer(DstNormalization.STANDARD_DST_I);
-        double result[], tolerance = 1E-12;
+        FastSineTransform transformer;
+        double result[], tolerance = 1e-12;
 
-        double x[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 };
-        double y[] = { 0.0, 20.1093579685034, -9.65685424949238,
-                       5.98642305066196, -4.0, 2.67271455167720,
-                      -1.65685424949238, 0.795649469518633 };
+        final double x[] = {
+            0, 1, 2, 3, 4, 5, 6, 7
+        };
+        final double y[] = {
+            0.0, 20.1093579685034, -9.65685424949238,
+            5.98642305066196, -4.0, 2.67271455167720,
+            -1.65685424949238, 0.795649469518633
+        };
 
-        result = transformer.transform(x, TransformType.FORWARD);
+        transformer = new FastSineTransform(DstNormalization.STANDARD_DST_I);
+        result = transformer.apply(x);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(y[i], result[i], tolerance);
         }
 
-        result = transformer.transform(y, TransformType.INVERSE);
+        transformer = new FastSineTransform(DstNormalization.STANDARD_DST_I, true);
+        result = transformer.apply(y);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(x[i], result[i], tolerance);
         }
 
-        TransformUtils.scaleArray(x, FastMath.sqrt(x.length / 2.0));
-        transformer = new FastSineTransformer(DstNormalization.ORTHOGONAL_DST_I);
+        TransformUtils.scaleInPlace(x, Math.sqrt(x.length / 2d));
+        transformer = new FastSineTransform(DstNormalization.ORTHOGONAL_DST_I);
 
-        result = transformer.transform(y, TransformType.FORWARD);
+        result = transformer.apply(y);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(x[i], result[i], tolerance);
         }
 
-        result = transformer.transform(x, TransformType.INVERSE);
+        transformer = new FastSineTransform(DstNormalization.ORTHOGONAL_DST_I, true);
+        result = transformer.apply(x);
         for (int i = 0; i < result.length; i++) {
             Assert.assertEquals(y[i], result[i], tolerance);
         }
@@ -246,20 +242,22 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
      */
     @Test
     public void testSinFunction() {
-        UnivariateFunction f = new Sin();
-        FastSineTransformer transformer;
-        transformer = new FastSineTransformer(DstNormalization.STANDARD_DST_I);
-        double min, max, result[], tolerance = 1E-12; int N = 1 << 8;
-
-        min = 0.0; max = 2.0 * FastMath.PI;
-        result = transformer.transform(f, min, max, N, TransformType.FORWARD);
+        final UnivariateFunction sinFunction = new Sin();
+        final DoubleUnaryOperator f = (x) -> sinFunction.value(x);
+        final FastSineTransform transformer = new FastSineTransform(DstNormalization.STANDARD_DST_I);
+        double min, max, result[], tolerance = 1e-12; int N = 1 << 8;
+
+        min = 0.0;
+        max = 2 * Math.PI;
+        result = transformer.apply(f, min, max, N);
         Assert.assertEquals(N >> 1, result[2], tolerance);
         for (int i = 0; i < N; i += (i == 1 ? 2 : 1)) {
             Assert.assertEquals(0.0, result[i], tolerance);
         }
 
-        min = -FastMath.PI; max = FastMath.PI;
-        result = transformer.transform(f, min, max, N, TransformType.FORWARD);
+        min = -Math.PI;
+        max = Math.PI;
+        result = transformer.apply(f, min, max, N);
         Assert.assertEquals(-(N >> 1), result[2], tolerance);
         for (int i = 0; i < N; i += (i == 1 ? 2 : 1)) {
             Assert.assertEquals(0.0, result[i], tolerance);
@@ -271,29 +269,29 @@ public final class FastSineTransformerTest extends RealTransformerAbstractTest {
      */
     @Test
     public void testParameters() throws Exception {
-        UnivariateFunction f = new Sin();
-        FastSineTransformer transformer;
-        transformer = new FastSineTransformer(DstNormalization.STANDARD_DST_I);
+        final UnivariateFunction sinFunction = new Sin();
+        final DoubleUnaryOperator f = (x) -> sinFunction.value(x);
+        final FastSineTransform transformer = new FastSineTransform(DstNormalization.STANDARD_DST_I);
 
         try {
             // bad interval
-            transformer.transform(f, 1, -1, 64, TransformType.FORWARD);
-            Assert.fail("Expecting MathIllegalArgumentException - bad interval");
-        } catch (MathIllegalArgumentException ex) {
+            transformer.apply(f, 1, -1, 64);
+            Assert.fail("Expecting IllegalArgumentException - bad interval");
+        } catch (IllegalArgumentException ex) {
             // expected
         }
         try {
             // bad samples number
-            transformer.transform(f, -1, 1, 0, TransformType.FORWARD);
-            Assert.fail("Expecting MathIllegalArgumentException - bad samples number");
-        } catch (MathIllegalArgumentException ex) {
+            transformer.apply(f, -1, 1, 0);
+            Assert.fail("Expecting IllegalArgumentException - bad samples number");
+        } catch (IllegalArgumentException ex) {
             // expected
         }
         try {
             // bad samples number
-            transformer.transform(f, -1, 1, 100, TransformType.FORWARD);
-            Assert.fail("Expecting MathIllegalArgumentException - bad samples number");
-        } catch (MathIllegalArgumentException ex) {
+            transformer.apply(f, -1, 1, 100);
+            Assert.fail("Expecting IllegalArgumentException - bad samples number");
+        } catch (IllegalArgumentException ex) {
             // expected
         }
     }
diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/RealTransformerAbstractTest.java b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/RealTransformerAbstractTest.java
similarity index 57%
rename from commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/RealTransformerAbstractTest.java
rename to commons-math-transform/src/test/java/org/apache/commons/math4/transform/RealTransformerAbstractTest.java
index a28d29c..a419110 100644
--- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/transform/RealTransformerAbstractTest.java
+++ b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/RealTransformerAbstractTest.java
@@ -14,40 +14,38 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.math4.legacy.transform;
+package org.apache.commons.math4.transform;
 
-import java.util.Random;
-
-import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
-import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
-import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
-import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
-import org.apache.commons.math4.legacy.util.FastMath;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.function.DoubleUnaryOperator;
+
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+
 /**
- * Abstract test for classes implementing the {@link RealTransformer} interface.
+ * Abstract test for classes implementing the {@link RealTransform} interface.
  * This abstract test handles the automatic generation of random data of various
  * sizes. For each generated data array, actual values (returned by the
  * transformer to be tested) are compared to expected values, returned by the
- * {@link #transform(double[], TransformType)} (to be implemented by the user:
- * a naive method may be used). Methods are also provided to test that invalid
- * parameters throw the expected exceptions.
+ * {@link #apply(double[])} (to be implemented by the user: a naive method may
+ * be used). Methods are also provided to test that invalid parameters throw the
+ * expected exceptions.
  *
  * @since 3.0
  */
 public abstract class RealTransformerAbstractTest {
-
-    /** The common seed of all random number generators used in this test. */
-    private final static long SEED = 20110119L;
+    /** RNG. */
+    private static final UniformRandomProvider RNG = RandomSource.create(RandomSource.MWC_256);
 
     /**
-     * Returns a new instance of the {@link RealTransformer} to be tested.
+     * Returns a new instance of the {@link RealTransform} to be tested.
      *
+     * @param inverse Whether to apply the inverse transform.
      * @return a the transformer to be tested
      */
-    abstract RealTransformer createRealTransformer();
+    abstract RealTransform createRealTransformer(boolean inverse);
 
     /**
      * Returns an invalid data size. Transforms with this data size should
@@ -62,7 +60,7 @@ public abstract class RealTransformerAbstractTest {
     /**
      * Returns the total number of invalid data sizes to be tested. If data
      * array of any
-     * size can be handled by the {@link RealTransformer} to be tested, this
+     * size can be handled by the {@link RealTransform} to be tested, this
      * method should return {@code 0}.
      *
      * @return the total number of invalid data sizes
@@ -98,17 +96,17 @@ public abstract class RealTransformerAbstractTest {
 
     /**
      * Returns a function for the accuracy check of
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
+     * {@link RealTransform#apply(DoubleUnaryOperator, double, double, int)}.
      * This function should be valid. In other words, none of the above methods
      * should throw an exception when passed this function.
      *
      * @return a valid function
      */
-    abstract UnivariateFunction getValidFunction();
+    abstract DoubleUnaryOperator getValidFunction();
 
     /**
      * Returns a sampling lower bound for the accuracy check of
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
+     * {@link RealTransform#apply(DoubleUnaryOperator, double, double, int)}.
      * This lower bound should be valid. In other words, none of the above
      * methods should throw an exception when passed this bound.
      *
@@ -118,7 +116,7 @@ public abstract class RealTransformerAbstractTest {
 
     /**
      * Returns a sampling upper bound for the accuracy check of
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
+     * {@link RealTransform#apply(DoubleUnaryOperator, double, double, int, TransformType)}.
      * This upper bound should be valid. In other words, none of the above
      * methods should throw an exception when passed this bound.
      *
@@ -129,31 +127,28 @@ public abstract class RealTransformerAbstractTest {
     /**
      * Returns the expected transform of the specified real data array.
      *
-     * @param x the real data array to be transformed
-     * @param type the type of transform (forward, inverse) to be performed
-     * @return the expected transform
+     * @param x Data to be transformed.
+     * @param type Whether to perform the inverse) transform.
+     * @return the expected transform.
      */
-    abstract double[] transform(double[] x, TransformType type);
+    abstract double[] transform(double[] x, boolean type);
 
-    /*
-     * Check of preconditions.
-     */
+    // Check of preconditions.
 
     /**
-     * {@link RealTransformer#transform(double[], TransformType)} should throw a
-     * {@link MathIllegalArgumentException} if data size is invalid.
+     * {@link RealTransform#apply(double[])} should throw a
+     * {@link IllegalArgumentException} if data size is invalid.
      */
     @Test
     public void testTransformRealInvalidDataSize() {
-        final TransformType[] type = TransformType.values();
-        final RealTransformer transformer = createRealTransformer();
         for (int i = 0; i < getNumberOfInvalidDataSizes(); i++) {
             final int n = getInvalidDataSize(i);
-            for (int j = 0; j < type.length; j++) {
+            for (boolean type : new boolean[] { true, false }) {
                 try {
-                    transformer.transform(createRealData(n), type[j]);
-                    Assert.fail(type[j] + ", " + n);
-                } catch (MathIllegalArgumentException e) {
+                    final RealTransform transformer = createRealTransformer(type);
+                    transformer.apply(createRealData(n));
+                    Assert.fail(type + ", " + n);
+                } catch (IllegalArgumentException e) {
                     // Expected: do nothing
                 }
             }
@@ -161,24 +156,23 @@ public abstract class RealTransformerAbstractTest {
     }
 
     /**
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}
-     * should throw a {@link MathIllegalArgumentException} if number of samples
-     * is invalid.
+     * {@link RealTransformer#(DoubleUnaryOperator, double, double, int)}
+     * should throw {@link IllegalArgumentException} if number of samples is
+     * invalid.
      */
     @Test
     public void testTransformFunctionInvalidDataSize() {
-        final TransformType[] type = TransformType.values();
-        final RealTransformer transformer = createRealTransformer();
-        final UnivariateFunction f = getValidFunction();
+        final DoubleUnaryOperator f = getValidFunction();
         final double a = getValidLowerBound();
         final double b = getValidUpperBound();
         for (int i = 0; i < getNumberOfInvalidDataSizes(); i++) {
             final int n = getInvalidDataSize(i);
-            for (int j = 0; j < type.length; j++) {
+            for (boolean type : new boolean[] { true, false }) {
                 try {
-                    transformer.transform(f, a, b, n, type[j]);
-                    Assert.fail(type[j] + ", " + n);
-                } catch (MathIllegalArgumentException e) {
+                    final RealTransform transformer = createRealTransformer(type);
+                    transformer.apply(f, a, b, n);
+                    Assert.fail(type + ", " + n);
+                } catch (IllegalArgumentException e) {
                     // Expected: do nothing
                 }
             }
@@ -186,24 +180,23 @@ public abstract class RealTransformerAbstractTest {
     }
 
     /**
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}
-     * should throw a {@link NotStrictlyPositiveException} if number of samples
+     * {@link RealTransform#apply(DoubleUnaryOperator, double, double, int)}
+     * should throw {@link IllegalArgumentException} if number of samples
      * is not strictly positive.
      */
     @Test
     public void testTransformFunctionNotStrictlyPositiveNumberOfSamples() {
-        final TransformType[] type = TransformType.values();
-        final RealTransformer transformer = createRealTransformer();
-        final UnivariateFunction f = getValidFunction();
+        final DoubleUnaryOperator f = getValidFunction();
         final double a = getValidLowerBound();
         final double b = getValidUpperBound();
         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
             final int n = getValidDataSize(i);
-            for (int j = 0; j < type.length; j++) {
+            for (boolean type : new boolean[] { true, false }) {
                 try {
-                    transformer.transform(f, a, b, -n, type[j]);
-                    Assert.fail(type[j] + ", " + (-n));
-                } catch (NotStrictlyPositiveException e) {
+                    final RealTransform transformer = createRealTransformer(type);
+                    transformer.apply(f, a, b, -n);
+                    Assert.fail(type + ", " + (-n));
+                } catch (IllegalArgumentException e) {
                     // Expected: do nothing
                 }
             }
@@ -211,133 +204,124 @@ public abstract class RealTransformerAbstractTest {
     }
 
     /**
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}
-     * should throw a {@link NumberIsTooLargeException} if sampling bounds are
+     * {@link RealTransform#apply(DoubleUnaryOperator, double, double, int)}
+     * should throw {@link IllegalArgumentException} if sampling bounds are
      * not correctly ordered.
      */
     @Test
     public void testTransformFunctionInvalidBounds() {
-        final TransformType[] type = TransformType.values();
-        final RealTransformer transformer = createRealTransformer();
-        final UnivariateFunction f = getValidFunction();
+        final DoubleUnaryOperator f = getValidFunction();
         final double a = getValidLowerBound();
         final double b = getValidUpperBound();
         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
             final int n = getValidDataSize(i);
-            for (int j = 0; j < type.length; j++) {
+            for (boolean type : new boolean[] { true, false }) {
                 try {
-                    transformer.transform(f, b, a, n, type[j]);
-                    Assert.fail(type[j] + ", " + b + ", " + a);
-                } catch (NumberIsTooLargeException e) {
+                    final RealTransform transformer = createRealTransformer(type);
+                    transformer.apply(f, b, a, n);
+                    Assert.fail(type + ", " + b + ", " + a);
+                } catch (IllegalArgumentException e) {
                     // Expected: do nothing
                 }
             }
         }
     }
 
-    /*
-     * Accuracy tests of transform of valid data.
-     */
+    // Accuracy tests of transform of valid data.
 
     /**
-     * Accuracy check of {@link RealTransformer#transform(double[], TransformType)}.
+     * Accuracy check of {@link RealTransform#apply(double[])}.
      * For each valid data size returned by
      * {@link #getValidDataSize(int) getValidDataSize(i)},
      * a random data array is generated with
      * {@link #createRealData(int) createRealData(i)}. The actual
      * transform is computed and compared to the expected transform, return by
-     * {@link #transform(double[], TransformType)}. Actual and expected values
+     * {@link #transform(double[], boolean)}. Actual and expected values
      * should be equal to within the relative error returned by
      * {@link #getRelativeTolerance(int) getRelativeTolerance(i)}.
      */
     @Test
     public void testTransformReal() {
-        final TransformType[] type = TransformType.values();
         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
             final int n = getValidDataSize(i);
             final double tol = getRelativeTolerance(i);
-            for (int j = 0; j < type.length; j++) {
-                doTestTransformReal(n, tol, type[j]);
+            for (boolean type : new boolean[] { true, false }) {
+                doTestTransformReal(n, tol, type);
             }
         }
     }
 
     /**
      * Accuracy check of
-     * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
+     * {@link RealTransform#apply(DoubleUnaryOperator, double, double, int, TransformType)}.
      * For each valid data size returned by
      * {@link #getValidDataSize(int) getValidDataSize(i)},
      * the {@link UnivariateFunction} returned by {@link #getValidFunction()} is
      * sampled. The actual transform is computed and compared to the expected
-     * transform, return by {@link #transform(double[], TransformType)}. Actual
+     * transform, return by {@link #transform(double[], boolean)}. Actual
      * and expected values should be equal to within the relative error returned
      * by {@link #getRelativeTolerance(int) getRelativeTolerance(i)}.
      */
     @Test
     public void testTransformFunction() {
-        final TransformType[] type = TransformType.values();
         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
             final int n = getValidDataSize(i);
             final double tol = getRelativeTolerance(i);
-            for (int j = 0; j < type.length; j++) {
-                doTestTransformFunction(n, tol, type[j]);
+            for (boolean type : new boolean[] { true, false }) {
+                doTestTransformFunction(n, tol, type);
             }
         }
     }
 
-    /*
-     * Utility methods.
-     */
+    // Utility methods.
 
     /**
-     * Returns a random array of doubles. Random generator always uses the same
-     * seed.
+     * Returns a random array of doubles.
      *
      * @param n the size of the array to be returned
      * @return a random array of specified size
      */
     double[] createRealData(final int n) {
-        final Random random = new Random(SEED);
         final double[] data = new double[n];
         for (int i = 0; i < n; i++) {
-            data[i] = 2.0 * random.nextDouble() - 1.0;
+            data[i] = 2 * RNG.nextDouble() - 1;
         }
         return data;
     }
 
-    /*
-     * The tests per se.
-     */
+    // Actual tests.
 
-    private void doTestTransformReal(final int n, final double tol,
-        final TransformType type) {
-        final RealTransformer transformer = createRealTransformer();
+    private void doTestTransformReal(final int n,
+                                     final double tol,
+                                     final boolean type) {
+        final RealTransform transformer = createRealTransformer(type);
         final double[] x = createRealData(n);
         final double[] expected = transform(x, type);
-        final double[] actual = transformer.transform(x, type);
+        final double[] actual = transformer.apply(x);
         for (int i = 0; i < n; i++) {
             final String msg = String.format("%d, %d", n, i);
-            final double delta = tol * FastMath.abs(expected[i]);
+            final double delta = tol * Math.abs(expected[i]);
             Assert.assertEquals(msg, expected[i], actual[i], delta);
         }
     }
 
-    private void doTestTransformFunction(final int n, final double tol,
-        final TransformType type) {
-        final RealTransformer transformer = createRealTransformer();
-        final UnivariateFunction f = getValidFunction();
+    private void doTestTransformFunction(final int n,
+                                         final double tol,
+                                         final boolean type) {
+        final RealTransform transformer = createRealTransformer(type);
+        final DoubleUnaryOperator f = getValidFunction();
         final double a = getValidLowerBound();
         final double b = getValidUpperBound();
         final double[] x = createRealData(n);
         for (int i = 0; i < n; i++) {
             final double t = a + i * (b - a) / n;
-            x[i] = f.value(t);
+            x[i] = f.applyAsDouble(t);
         }
         final double[] expected = transform(x, type);
-        final double[] actual = transformer.transform(f, a, b, n, type);
+        final double[] actual = transformer.apply(f, a, b, n);
         for (int i = 0; i < n; i++) {
             final String msg = String.format("%d, %d", n, i);
-            final double delta = tol * FastMath.abs(expected[i]);
+            final double delta = tol * Math.abs(expected[i]);
             Assert.assertEquals(msg, expected[i], actual[i], delta);
         }
     }
diff --git a/commons-math-transform/src/test/java/org/apache/commons/math4/transform/TransformUtilsTest.java b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/TransformUtilsTest.java
new file mode 100644
index 0000000..817ae69
--- /dev/null
+++ b/commons-math-transform/src/test/java/org/apache/commons/math4/transform/TransformUtilsTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.math4.transform;
+
+import java.util.function.DoubleUnaryOperator;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.commons.math3.analysis.function.Sin;
+
+/**
+ * Tests for {@link TransformUtils}.
+ */
+public class TransformUtilsTest {
+    private static final Sin SIN_FUNCTION = new Sin();
+    private static final DoubleUnaryOperator SIN = (x) -> SIN_FUNCTION.value(x);
+
+    @Test(expected = TransformException.class)
+    public void testSampleWrongBounds(){
+        TransformUtils.sample(SIN, Math.PI, 0.0, 10);
+    }
+
+    @Test(expected = TransformException.class)
+    public void testSampleNegativeNumberOfPoints(){
+        TransformUtils.sample(SIN, 0.0, Math.PI, -1);
+    }
+
+    @Test(expected = TransformException.class)
+    public void testSampleNullNumberOfPoints(){
+        TransformUtils.sample(SIN, 0.0, Math.PI, 0);
+    }
+
+    @Test
+    public void testSample() {
+        final int n = 11;
+        final double min = 0.0;
+        final double max = Math.PI;
+        final double[] actual = TransformUtils.sample(SIN, min, max, n);
+        for (int i = 0; i < n; i++) {
+            final double x = min + (max - min) / n * i;
+            Assert.assertEquals("x = " + x, Math.sin(x), actual[i], 1e-15);
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index e8c4149..134c52b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,6 +107,7 @@
   <modules>
     <!-- Modules that do not depend on "legacy" codes. -->
     <module>commons-math-neuralnet</module>
+    <module>commons-math-transform</module>
 
     <!-- Non-modularized functionalities. -->
     <module>commons-math-legacy</module>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a9f80b2..2019541 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -54,6 +54,9 @@ If the output is not quite correct, check for invisible trailing spaces!
     </release>
 
     <release version="4.0" date="XXXX-XX-XX" description="">
+      <action dev="erans" type="update" issue="MATH-1582">
+        Transforms codes moved into a dedicated maven module.
+      </action>
       <action dev="erans" type="update" issue="MATH-1578">
         ANN codes moved into a dedicated maven module.
       </action>