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.  *      http://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. package org.apache.commons.math3.complex;

  18. import java.io.Serializable;

  19. import org.apache.commons.math3.exception.MathIllegalArgumentException;
  20. import org.apache.commons.math3.exception.MathIllegalStateException;
  21. import org.apache.commons.math3.exception.OutOfRangeException;
  22. import org.apache.commons.math3.exception.ZeroException;
  23. import org.apache.commons.math3.exception.util.LocalizedFormats;
  24. import org.apache.commons.math3.util.FastMath;

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

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

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

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

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

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

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

  55.     /**
  56.      * Build an engine for computing the {@code n}-th roots of unity.
  57.      */
  58.     public RootsOfUnity() {

  59.         omegaCount = 0;
  60.         omegaReal = null;
  61.         omegaImaginaryCounterClockwise = null;
  62.         omegaImaginaryClockwise = null;
  63.         isCounterClockWise = true;
  64.     }

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

  77.         if (omegaCount == 0) {
  78.             throw new MathIllegalStateException(
  79.                     LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
  80.         }
  81.         return isCounterClockWise;
  82.     }

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

  103.         if (n == 0) {
  104.             throw new ZeroException(
  105.                     LocalizedFormats.CANNOT_COMPUTE_0TH_ROOT_OF_UNITY);
  106.         }

  107.         isCounterClockWise = n > 0;

  108.         // avoid repetitive calculations
  109.         final int absN = FastMath.abs(n);

  110.         if (absN == omegaCount) {
  111.             return;
  112.         }

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

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

  143.         if (omegaCount == 0) {
  144.             throw new MathIllegalStateException(
  145.                     LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
  146.         }
  147.         if ((k < 0) || (k >= omegaCount)) {
  148.             throw new OutOfRangeException(
  149.                     LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX,
  150.                     Integer.valueOf(k),
  151.                     Integer.valueOf(0),
  152.                     Integer.valueOf(omegaCount - 1));
  153.         }

  154.         return omegaReal[k];
  155.     }

  156.     /**
  157.      * Get the imaginary part of the {@code k}-th {@code n}-th root of unity.
  158.      *
  159.      * @param k index of the {@code n}-th root of unity
  160.      * @return imaginary part of the {@code k}-th {@code n}-th root of unity
  161.      * @throws MathIllegalStateException if no roots of unity have been
  162.      * computed yet
  163.      * @throws OutOfRangeException if {@code k} is out of range
  164.      */
  165.     public synchronized double getImaginary(int k)
  166.             throws MathIllegalStateException, OutOfRangeException {

  167.         if (omegaCount == 0) {
  168.             throw new MathIllegalStateException(
  169.                     LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
  170.         }
  171.         if ((k < 0) || (k >= omegaCount)) {
  172.             throw new OutOfRangeException(
  173.                     LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX,
  174.                     Integer.valueOf(k),
  175.                     Integer.valueOf(0),
  176.                     Integer.valueOf(omegaCount - 1));
  177.         }

  178.         return isCounterClockWise ? omegaImaginaryCounterClockwise[k] :
  179.             omegaImaginaryClockwise[k];
  180.     }

  181.     /**
  182.      * Returns the number of roots of unity currently stored. If
  183.      * {@link #computeRoots(int)} was called with {@code n}, then this method
  184.      * returns {@code abs(n)}. If no roots of unity have been computed yet, this
  185.      * method returns 0.
  186.      *
  187.      * @return the number of roots of unity currently stored
  188.      */
  189.     public synchronized int getNumberOfRoots() {
  190.         return omegaCount;
  191.     }
  192. }