001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.math3.complex; 018 019 import java.io.Serializable; 020 021 import org.apache.commons.math3.exception.MathIllegalArgumentException; 022 import org.apache.commons.math3.exception.MathIllegalStateException; 023 import org.apache.commons.math3.exception.OutOfRangeException; 024 import org.apache.commons.math3.exception.ZeroException; 025 import org.apache.commons.math3.exception.util.LocalizedFormats; 026 import org.apache.commons.math3.util.FastMath; 027 028 /** 029 * A helper class for the computation and caching of the {@code n}-th roots of 030 * unity. 031 * 032 * @version $Id: RootsOfUnity.java 1416643 2012-12-03 19:37:14Z tn $ 033 * @since 3.0 034 */ 035 public class RootsOfUnity implements Serializable { 036 037 /** Serializable version id. */ 038 private static final long serialVersionUID = 20120201L; 039 040 /** Number of roots of unity. */ 041 private int omegaCount; 042 043 /** Real part of the roots. */ 044 private double[] omegaReal; 045 046 /** 047 * Imaginary part of the {@code n}-th roots of unity, for positive values 048 * of {@code n}. In this array, the roots are stored in counter-clockwise 049 * order. 050 */ 051 private double[] omegaImaginaryCounterClockwise; 052 053 /** 054 * Imaginary part of the {@code n}-th roots of unity, for negative values 055 * of {@code n}. In this array, the roots are stored in clockwise order. 056 */ 057 private double[] omegaImaginaryClockwise; 058 059 /** 060 * {@code true} if {@link #computeRoots(int)} was called with a positive 061 * value of its argument {@code n}. In this case, counter-clockwise ordering 062 * of the roots of unity should be used. 063 */ 064 private boolean isCounterClockWise; 065 066 /** 067 * Build an engine for computing the {@code n}-th roots of unity. 068 */ 069 public RootsOfUnity() { 070 071 omegaCount = 0; 072 omegaReal = null; 073 omegaImaginaryCounterClockwise = null; 074 omegaImaginaryClockwise = null; 075 isCounterClockWise = true; 076 } 077 078 /** 079 * Returns {@code true} if {@link #computeRoots(int)} was called with a 080 * positive value of its argument {@code n}. If {@code true}, then 081 * counter-clockwise ordering of the roots of unity should be used. 082 * 083 * @return {@code true} if the roots of unity are stored in 084 * counter-clockwise order 085 * @throws MathIllegalStateException if no roots of unity have been computed 086 * yet 087 */ 088 public synchronized boolean isCounterClockWise() 089 throws MathIllegalStateException { 090 091 if (omegaCount == 0) { 092 throw new MathIllegalStateException( 093 LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET); 094 } 095 return isCounterClockWise; 096 } 097 098 /** 099 * <p> 100 * Computes the {@code n}-th roots of unity. The roots are stored in 101 * {@code omega[]}, such that {@code omega[k] = w ^ k}, where 102 * {@code k = 0, ..., n - 1}, {@code w = exp(2 * pi * i / n)} and 103 * {@code i = sqrt(-1)}. 104 * </p> 105 * <p> 106 * Note that {@code n} can be positive of negative 107 * </p> 108 * <ul> 109 * <li>{@code abs(n)} is always the number of roots of unity.</li> 110 * <li>If {@code n > 0}, then the roots are stored in counter-clockwise order.</li> 111 * <li>If {@code n < 0}, then the roots are stored in clockwise order.</p> 112 * </ul> 113 * 114 * @param n the (signed) number of roots of unity to be computed 115 * @throws ZeroException if {@code n = 0} 116 */ 117 public synchronized void computeRoots(int n) throws ZeroException { 118 119 if (n == 0) { 120 throw new ZeroException( 121 LocalizedFormats.CANNOT_COMPUTE_0TH_ROOT_OF_UNITY); 122 } 123 124 isCounterClockWise = n > 0; 125 126 // avoid repetitive calculations 127 final int absN = FastMath.abs(n); 128 129 if (absN == omegaCount) { 130 return; 131 } 132 133 // calculate everything from scratch 134 final double t = 2.0 * FastMath.PI / absN; 135 final double cosT = FastMath.cos(t); 136 final double sinT = FastMath.sin(t); 137 omegaReal = new double[absN]; 138 omegaImaginaryCounterClockwise = new double[absN]; 139 omegaImaginaryClockwise = new double[absN]; 140 omegaReal[0] = 1.0; 141 omegaImaginaryCounterClockwise[0] = 0.0; 142 omegaImaginaryClockwise[0] = 0.0; 143 for (int i = 1; i < absN; i++) { 144 omegaReal[i] = omegaReal[i - 1] * cosT - 145 omegaImaginaryCounterClockwise[i - 1] * sinT; 146 omegaImaginaryCounterClockwise[i] = omegaReal[i - 1] * sinT + 147 omegaImaginaryCounterClockwise[i - 1] * cosT; 148 omegaImaginaryClockwise[i] = -omegaImaginaryCounterClockwise[i]; 149 } 150 omegaCount = absN; 151 } 152 153 /** 154 * Get the real part of the {@code k}-th {@code n}-th root of unity. 155 * 156 * @param k index of the {@code n}-th root of unity 157 * @return real part of the {@code k}-th {@code n}-th root of unity 158 * @throws MathIllegalStateException if no roots of unity have been 159 * computed yet 160 * @throws MathIllegalArgumentException if {@code k} is out of range 161 */ 162 public synchronized double getReal(int k) 163 throws MathIllegalStateException, MathIllegalArgumentException { 164 165 if (omegaCount == 0) { 166 throw new MathIllegalStateException( 167 LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET); 168 } 169 if ((k < 0) || (k >= omegaCount)) { 170 throw new OutOfRangeException( 171 LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX, 172 Integer.valueOf(k), 173 Integer.valueOf(0), 174 Integer.valueOf(omegaCount - 1)); 175 } 176 177 return omegaReal[k]; 178 } 179 180 /** 181 * Get the imaginary part of the {@code k}-th {@code n}-th root of unity. 182 * 183 * @param k index of the {@code n}-th root of unity 184 * @return imaginary part of the {@code k}-th {@code n}-th root of unity 185 * @throws MathIllegalStateException if no roots of unity have been 186 * computed yet 187 * @throws OutOfRangeException if {@code k} is out of range 188 */ 189 public synchronized double getImaginary(int k) 190 throws MathIllegalStateException, OutOfRangeException { 191 192 if (omegaCount == 0) { 193 throw new MathIllegalStateException( 194 LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET); 195 } 196 if ((k < 0) || (k >= omegaCount)) { 197 throw new OutOfRangeException( 198 LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX, 199 Integer.valueOf(k), 200 Integer.valueOf(0), 201 Integer.valueOf(omegaCount - 1)); 202 } 203 204 return isCounterClockWise ? omegaImaginaryCounterClockwise[k] : 205 omegaImaginaryClockwise[k]; 206 } 207 208 /** 209 * Returns the number of roots of unity currently stored. If 210 * {@link #computeRoots(int)} was called with {@code n}, then this method 211 * returns {@code abs(n)}. If no roots of unity have been computed yet, this 212 * method returns 0. 213 * 214 * @return the number of roots of unity currently stored 215 */ 216 public synchronized int getNumberOfRoots() { 217 return omegaCount; 218 } 219 }