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 */ 017package org.apache.commons.math3.complex; 018 019import java.io.Serializable; 020 021import org.apache.commons.math3.exception.MathIllegalArgumentException; 022import org.apache.commons.math3.exception.MathIllegalStateException; 023import org.apache.commons.math3.exception.OutOfRangeException; 024import org.apache.commons.math3.exception.ZeroException; 025import org.apache.commons.math3.exception.util.LocalizedFormats; 026import 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 * @since 3.0 033 */ 034public class RootsOfUnity implements Serializable { 035 036 /** Serializable version id. */ 037 private static final long serialVersionUID = 20120201L; 038 039 /** Number of roots of unity. */ 040 private int omegaCount; 041 042 /** Real part of the roots. */ 043 private double[] omegaReal; 044 045 /** 046 * Imaginary part of the {@code n}-th roots of unity, for positive values 047 * of {@code n}. In this array, the roots are stored in counter-clockwise 048 * order. 049 */ 050 private double[] omegaImaginaryCounterClockwise; 051 052 /** 053 * Imaginary part of the {@code n}-th roots of unity, for negative values 054 * of {@code n}. In this array, the roots are stored in clockwise order. 055 */ 056 private double[] omegaImaginaryClockwise; 057 058 /** 059 * {@code true} if {@link #computeRoots(int)} was called with a positive 060 * value of its argument {@code n}. In this case, counter-clockwise ordering 061 * of the roots of unity should be used. 062 */ 063 private boolean isCounterClockWise; 064 065 /** 066 * Build an engine for computing the {@code n}-th roots of unity. 067 */ 068 public RootsOfUnity() { 069 070 omegaCount = 0; 071 omegaReal = null; 072 omegaImaginaryCounterClockwise = null; 073 omegaImaginaryClockwise = null; 074 isCounterClockWise = true; 075 } 076 077 /** 078 * Returns {@code true} if {@link #computeRoots(int)} was called with a 079 * positive value of its argument {@code n}. If {@code true}, then 080 * counter-clockwise ordering of the roots of unity should be used. 081 * 082 * @return {@code true} if the roots of unity are stored in 083 * counter-clockwise order 084 * @throws MathIllegalStateException if no roots of unity have been computed 085 * yet 086 */ 087 public synchronized boolean isCounterClockWise() 088 throws MathIllegalStateException { 089 090 if (omegaCount == 0) { 091 throw new MathIllegalStateException( 092 LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET); 093 } 094 return isCounterClockWise; 095 } 096 097 /** 098 * <p> 099 * Computes the {@code n}-th roots of unity. The roots are stored in 100 * {@code omega[]}, such that {@code omega[k] = w ^ k}, where 101 * {@code k = 0, ..., n - 1}, {@code w = exp(2 * pi * i / n)} and 102 * {@code i = sqrt(-1)}. 103 * </p> 104 * <p> 105 * Note that {@code n} can be positive of negative 106 * </p> 107 * <ul> 108 * <li>{@code abs(n)} is always the number of roots of unity.</li> 109 * <li>If {@code n > 0}, then the roots are stored in counter-clockwise order.</li> 110 * <li>If {@code n < 0}, then the roots are stored in clockwise order.</p> 111 * </ul> 112 * 113 * @param n the (signed) number of roots of unity to be computed 114 * @throws ZeroException if {@code n = 0} 115 */ 116 public synchronized void computeRoots(int n) throws ZeroException { 117 118 if (n == 0) { 119 throw new ZeroException( 120 LocalizedFormats.CANNOT_COMPUTE_0TH_ROOT_OF_UNITY); 121 } 122 123 isCounterClockWise = n > 0; 124 125 // avoid repetitive calculations 126 final int absN = FastMath.abs(n); 127 128 if (absN == omegaCount) { 129 return; 130 } 131 132 // calculate everything from scratch 133 final double t = 2.0 * FastMath.PI / absN; 134 final double cosT = FastMath.cos(t); 135 final double sinT = FastMath.sin(t); 136 omegaReal = new double[absN]; 137 omegaImaginaryCounterClockwise = new double[absN]; 138 omegaImaginaryClockwise = new double[absN]; 139 omegaReal[0] = 1.0; 140 omegaImaginaryCounterClockwise[0] = 0.0; 141 omegaImaginaryClockwise[0] = 0.0; 142 for (int i = 1; i < absN; i++) { 143 omegaReal[i] = omegaReal[i - 1] * cosT - 144 omegaImaginaryCounterClockwise[i - 1] * sinT; 145 omegaImaginaryCounterClockwise[i] = omegaReal[i - 1] * sinT + 146 omegaImaginaryCounterClockwise[i - 1] * cosT; 147 omegaImaginaryClockwise[i] = -omegaImaginaryCounterClockwise[i]; 148 } 149 omegaCount = absN; 150 } 151 152 /** 153 * Get the real part of the {@code k}-th {@code n}-th root of unity. 154 * 155 * @param k index of the {@code n}-th root of unity 156 * @return real part of the {@code k}-th {@code n}-th root of unity 157 * @throws MathIllegalStateException if no roots of unity have been 158 * computed yet 159 * @throws MathIllegalArgumentException if {@code k} is out of range 160 */ 161 public synchronized double getReal(int k) 162 throws MathIllegalStateException, MathIllegalArgumentException { 163 164 if (omegaCount == 0) { 165 throw new MathIllegalStateException( 166 LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET); 167 } 168 if ((k < 0) || (k >= omegaCount)) { 169 throw new OutOfRangeException( 170 LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX, 171 Integer.valueOf(k), 172 Integer.valueOf(0), 173 Integer.valueOf(omegaCount - 1)); 174 } 175 176 return omegaReal[k]; 177 } 178 179 /** 180 * Get the imaginary part of the {@code k}-th {@code n}-th root of unity. 181 * 182 * @param k index of the {@code n}-th root of unity 183 * @return imaginary part of the {@code k}-th {@code n}-th root of unity 184 * @throws MathIllegalStateException if no roots of unity have been 185 * computed yet 186 * @throws OutOfRangeException if {@code k} is out of range 187 */ 188 public synchronized double getImaginary(int k) 189 throws MathIllegalStateException, OutOfRangeException { 190 191 if (omegaCount == 0) { 192 throw new MathIllegalStateException( 193 LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET); 194 } 195 if ((k < 0) || (k >= omegaCount)) { 196 throw new OutOfRangeException( 197 LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX, 198 Integer.valueOf(k), 199 Integer.valueOf(0), 200 Integer.valueOf(omegaCount - 1)); 201 } 202 203 return isCounterClockWise ? omegaImaginaryCounterClockwise[k] : 204 omegaImaginaryClockwise[k]; 205 } 206 207 /** 208 * Returns the number of roots of unity currently stored. If 209 * {@link #computeRoots(int)} was called with {@code n}, then this method 210 * returns {@code abs(n)}. If no roots of unity have been computed yet, this 211 * method returns 0. 212 * 213 * @return the number of roots of unity currently stored 214 */ 215 public synchronized int getNumberOfRoots() { 216 return omegaCount; 217 } 218}