RootsOfUnity.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.complex;

  22. import java.io.Serializable;

  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.exception.MathIllegalStateException;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.SinCos;

  28. /**
  29.  * A helper class for the computation and caching of the {@code n}-th roots
  30.  * of unity.
  31.  */
  32. public class RootsOfUnity implements Serializable {

  33.     /** Serializable version id. */
  34.     private static final long serialVersionUID = 20120201L;

  35.     /** Number of roots of unity. */
  36.     private int omegaCount;

  37.     /** Real part of the roots. */
  38.     private double[] omegaReal;

  39.     /**
  40.      * Imaginary part of the {@code n}-th roots of unity, for positive values
  41.      * of {@code n}. In this array, the roots are stored in counter-clockwise
  42.      * order.
  43.      */
  44.     private double[] omegaImaginaryCounterClockwise;

  45.     /**
  46.      * Imaginary part of the {@code n}-th roots of unity, for negative values
  47.      * of {@code n}. In this array, the roots are stored in clockwise order.
  48.      */
  49.     private double[] omegaImaginaryClockwise;

  50.     /**
  51.      * {@code true} if {@link #computeRoots(int)} was called with a positive
  52.      * value of its argument {@code n}. In this case, counter-clockwise ordering
  53.      * of the roots of unity should be used.
  54.      */
  55.     private boolean isCounterClockWise;

  56.     /**
  57.      * Build an engine for computing the {@code n}-th roots of unity.
  58.      */
  59.     public RootsOfUnity() {
  60.         omegaCount = 0;
  61.         omegaReal = null;
  62.         omegaImaginaryCounterClockwise = null;
  63.         omegaImaginaryClockwise = null;
  64.         isCounterClockWise = true;
  65.     }

  66.     /**
  67.      * Returns {@code true} if {@link #computeRoots(int)} was called with a
  68.      * positive value of its argument {@code n}. If {@code true}, then
  69.      * counter-clockwise ordering of the roots of unity should be used.
  70.      *
  71.      * @return {@code true} if the roots of unity are stored in counter-clockwise order
  72.      * @throws MathIllegalStateException if no roots of unity have been computed yet
  73.      */
  74.     public boolean isCounterClockWise()
  75.             throws MathIllegalStateException {
  76.         synchronized (this) {
  77.             if (omegaCount == 0) {
  78.                 throw new MathIllegalStateException(LocalizedCoreFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
  79.             }
  80.             return isCounterClockWise;
  81.         }
  82.     }

  83.     /**
  84.      * Computes the {@code n}-th roots of unity.
  85.      * <p>
  86.      * The roots are stored in {@code omega[]}, such that {@code omega[k] = w ^ k},
  87.      * where {@code k = 0, ..., n - 1}, {@code w = exp(2 * pi * i / n)} and
  88.      * {@code i = sqrt(-1)}.
  89.      * <p>
  90.      * Note that {@code n} can be positive of negative
  91.      * <ul>
  92.      * <li>{@code abs(n)} is always the number of roots of unity.</li>
  93.      * <li>If {@code n > 0}, then the roots are stored in counter-clockwise order.</li>
  94.      * <li>If {@code n < 0}, then the roots are stored in clockwise order.</li>
  95.      * </ul>
  96.      *
  97.      * @param n the (signed) number of roots of unity to be computed
  98.      * @throws MathIllegalArgumentException if {@code n = 0}
  99.      */
  100.     public void computeRoots(int n) throws MathIllegalArgumentException {

  101.         if (n == 0) {
  102.             throw new MathIllegalArgumentException(LocalizedCoreFormats.CANNOT_COMPUTE_0TH_ROOT_OF_UNITY);
  103.         }

  104.         synchronized (this) {
  105.             isCounterClockWise = n > 0;

  106.             // avoid repetitive calculations
  107.             final int absN = FastMath.abs(n);

  108.             if (absN == omegaCount) {
  109.                 return;
  110.             }

  111.             // calculate everything from scratch
  112.             final double t  = 2.0 * FastMath.PI / absN;
  113.             final SinCos sc = FastMath.sinCos(t);
  114.             omegaReal = new double[absN];
  115.             omegaImaginaryCounterClockwise = new double[absN];
  116.             omegaImaginaryClockwise = new double[absN];
  117.             omegaReal[0] = 1.0;
  118.             omegaImaginaryCounterClockwise[0] = 0.0;
  119.             omegaImaginaryClockwise[0] = 0.0;
  120.             for (int i = 1; i < absN; i++) {
  121.                 omegaReal[i] = omegaReal[i - 1] * sc.cos() -
  122.                                 omegaImaginaryCounterClockwise[i - 1] * sc.sin();
  123.                 omegaImaginaryCounterClockwise[i] = omegaReal[i - 1] * sc.sin() +
  124.                                 omegaImaginaryCounterClockwise[i - 1] * sc.cos();
  125.                 omegaImaginaryClockwise[i] = -omegaImaginaryCounterClockwise[i];
  126.             }
  127.             omegaCount = absN;
  128.         }
  129.     }

  130.     /**
  131.      * Get the real part of the {@code k}-th {@code n}-th root of unity.
  132.      *
  133.      * @param k index of the {@code n}-th root of unity
  134.      * @return real part of the {@code k}-th {@code n}-th root of unity
  135.      * @throws MathIllegalStateException if no roots of unity have been computed yet
  136.      * @throws MathIllegalArgumentException if {@code k} is out of range
  137.      */
  138.     public double getReal(int k)
  139.             throws MathIllegalArgumentException, MathIllegalStateException {

  140.         synchronized (this) {
  141.             if (omegaCount == 0) {
  142.                 throw new MathIllegalStateException(LocalizedCoreFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
  143.             }
  144.             if ((k < 0) || (k >= omegaCount)) {
  145.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX,
  146.                                                        k, 0, omegaCount - 1);
  147.             }

  148.             return omegaReal[k];
  149.         }
  150.     }

  151.     /**
  152.      * Get the imaginary part of the {@code k}-th {@code n}-th root of unity.
  153.      *
  154.      * @param k index of the {@code n}-th root of unity
  155.      * @return imaginary part of the {@code k}-th {@code n}-th root of unity
  156.      * @throws MathIllegalStateException if no roots of unity have been computed yet
  157.      * @throws MathIllegalArgumentException if {@code k} is out of range
  158.      */
  159.     public double getImaginary(int k)
  160.             throws MathIllegalArgumentException, MathIllegalStateException {

  161.         synchronized (this) {
  162.             if (omegaCount == 0) {
  163.                 throw new MathIllegalStateException(LocalizedCoreFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
  164.             }
  165.             if ((k < 0) || (k >= omegaCount)) {
  166.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX,
  167.                                                        k, 0, omegaCount - 1);
  168.             }

  169.             return isCounterClockWise ?
  170.                    omegaImaginaryCounterClockwise[k] :
  171.                    omegaImaginaryClockwise[k];
  172.         }
  173.     }

  174.     /**
  175.      * Returns the number of roots of unity currently stored.
  176.      * <p>
  177.      * If {@link #computeRoots(int)} was called with {@code n}, then this method
  178.      * returns {@code abs(n)}. If no roots of unity have been computed yet, this
  179.      * method returns 0.
  180.      *
  181.      * @return the number of roots of unity currently stored
  182.      */
  183.     public int getNumberOfRoots() {
  184.         synchronized (this) {
  185.             return omegaCount;
  186.         }
  187.     }
  188. }