All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nom.tam.util.ComplexValue Maven / Gradle / Ivy

/*
 * #%L
 * nom.tam FITS library
 * %%
 * Copyright (C) 2004 - 2024 nom-tam-fits
 * %%
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * #L%
 */

package nom.tam.util;

import java.util.StringTokenizer;
import java.util.logging.Logger;

import nom.tam.fits.FitsFactory;
import nom.tam.fits.LongValueException;

/**
 * 

* A no-frills complex value, for representing complex numbers in FITS headers. It is a non-mutable object that is * created with a real and imaginary parts, which can be retrieved thereafter, and provides string formatting that is * suited specifically for representation in FITS headers. *

*

* Note that binary tables handle complex data differently, with elements of `float[2]` or `double[2]`. *

* * @author Attila Kovacs * * @since 1.16 */ public class ComplexValue { private static final Logger LOG = Logger.getLogger(ComplexValue.class.getName()); /** The complex zero **/ public static final ComplexValue ZERO = new ComplexValue(0.0, 0.0); /** The complex unity along the real axis, or (1.0, 0.0) **/ public static final ComplexValue ONE = new ComplexValue(1.0, 0.0); /** The unity along the imaginary axis i, or (0.0, 1.0) **/ public static final ComplexValue I = new ComplexValue(0.0, 1.0); /** The real and imaginary parts */ private double re, im; /** * The minimum size string needed to represent a complex value with even just single digits for the real and * imaginary parts. */ private static final int MIN_STRING_LENGTH = 5; // "(#,#)" /** * Private constructor */ private ComplexValue() { } /** * Instantiates a new complex number value with the specified real and imaginary components. * * @param re the real part * @param im thei maginary part */ public ComplexValue(double re, double im) { this(); this.re = re; this.im = im; } /** * Returns the real part of this complex value. * * @return the real part * * @see #im() */ public final double re() { return re; } /** * Returns the imaginary part of this complex value. * * @return the imaginary part * * @see #re() */ public final double im() { return im; } @Override public int hashCode() { return Double.hashCode(re()) ^ Double.hashCode(im()); } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof ComplexValue)) { return false; } ComplexValue z = (ComplexValue) o; return z.re() == re() && z.im() == im(); } /** * Checks if the complex value is zero. That is, if both the real or imaginary parts are zero. * * @return trueif both the real or imaginary parts are zero. Otherwise false. */ public final boolean isZero() { return re() == 0.0 && im() == 0.0; } /** * Checks if the complex value is finite. That is, if neither the real or imaginary parts are NaN or Infinite. * * @return trueif neither the real or imaginary parts are NaN or Infinite. Otherwise * false. */ public final boolean isFinite() { return Double.isFinite(re()) && Double.isFinite(im()); } @Override public String toString() { return "(" + re() + "," + im() + ")"; } /** * Converts this complex value to its string representation with up to the specified number of decimal places * showing after the leading figure, for both the real and imaginary parts. * * @param decimals the maximum number of decimal places to show. * * @return the string representation with the specified precision, which may be used in a FITS header. * * @see FlexFormat */ public String toString(int decimals) { FlexFormat f = new FlexFormat().setPrecision(decimals); return "(" + f.format(re()) + "," + f.format(im()) + ")"; } /** *

* Instantiates a new complex number value from the string repressentation of it in a FITS header value. By default, * it will parse complex numbers as a comma-separated pair of real values enclosed in a bracket, such as * (1.0, -2.0), or standard real values, such as 123.456 or 123 (as real-only * values). There can be any number of spaces around the brackets, number components or the comma. *

*

* If {@link FitsFactory#setAllowHeaderRepairs(boolean)} is set true, the parsing becomes more * tolerant, working around missing closing brackets, different number of comma-separated components, and missing * empty components. So, for example (,-1,abc may be parsed assuming it was meant to be -i. *

* * @param text The FITS header value representing the complex number, in brackets with the real * and imaginary pars separated by a comma. Additional spaces may surround the * component parts. * * @throws IllegalArgumentException if the supplied string does not appear to be a FITS standard representation of a * complex value. * * @see FitsFactory#setAllowHeaderRepairs(boolean) */ public ComplexValue(String text) throws IllegalArgumentException { this(); // Allow the use of 'D' or 'd' to mark the exponent, instead of the standard 'E' or 'e'... text = text.trim().toUpperCase().replace('D', 'E'); boolean hasOpeningBracket = text.charAt(0) == '('; boolean hasClosingBracket = text.charAt(text.length() - 1) == ')'; if (!(hasOpeningBracket || hasClosingBracket)) { // Use just the real value. re = Double.parseDouble(text); return; } if (!hasOpeningBracket || !hasClosingBracket) { if (!FitsFactory.isAllowHeaderRepairs()) { throw new IllegalArgumentException("Missing bracket around complex value: '" + text + "'\n\n --> Try FitsFactory.setAllowHeaderRepair(true).\n"); } LOG.warning("Ignored missing bracket in '" + text + "'."); } int start = hasOpeningBracket ? 1 : 0; int end = hasClosingBracket ? text.length() - 1 : text.length(); StringTokenizer tokens = new StringTokenizer(text.substring(start, end), FitsFactory.isAllowHeaderRepairs() ? ",; \t" : ", "); if (tokens.countTokens() != 2) { if (!FitsFactory.isAllowHeaderRepairs()) { throw new IllegalArgumentException( "Invalid complex value: '" + text + "'\n\n --> Try FitsFactory.setAllowHeaderRepair(true).\n"); } LOG.warning("Ignored wrong number of components (" + tokens.countTokens() + ") in '" + text + "'."); } if (tokens.hasMoreTokens()) { re = Double.parseDouble(tokens.nextToken()); } if (tokens.hasMoreTokens()) { im = Double.parseDouble(tokens.nextToken()); } } /** * Converts this comlex value to its string representation using up to the specified number of characters only. The * precision may be reduced as necessary to ensure that the representation fits in the allotted space. * * @param maxLength the maximum length of the returned string representation * * @return the string representation, possibly with reduced precision to fit into the alotted * space. * * @throws LongValueException if the space was too short to fit the value even with the minimal (1-digit) precision. */ public String toBoundedString(int maxLength) throws LongValueException { if (maxLength < MIN_STRING_LENGTH) { throw new LongValueException(maxLength, toString()); } String s = toString(); if (s.length() <= maxLength) { return s; } int decimals = FlexFormat.DOUBLE_DECIMALS; s = toString(decimals); while (s.length() > maxLength) { // Assume both real and imaginary parts shorten the same amount... decimals -= (s.length() - maxLength + 1) / 2; if (decimals < 0) { throw new LongValueException(maxLength, toString()); } s = toString(decimals); } return s; } /** * Converts this complex number to an array of 2. * * @return An array of 2 floating point values. * * @since 1.20 */ Object toArray() { return new double[] {re, im}; } /** * Single-precision complex values. * * @author Attila Kovacs * * @since 1.18 */ public static final class Float extends ComplexValue { /** * Instantiates a new single-precision complex number value with the specified real and imaginary components. * * @param re the real part * @param im thei maginary part * * @since 1.20 */ public Float(float re, float im) { super(re, im); } /** *

* Instantiates a new single-precision complex number value from the string repressentation of it in a FITS * header value. By default, it will parse complex numbers as a comma-separated pair of real values enclosed in * a bracket, such as (1.0, -2.0), or standard real values, such as 123.456 or * 123 (as real-only values). There can be any number of spaces around the brackets, number * components or the comma. *

* * @param str the FITS string representation of the complex value * * @throws IllegalArgumentException if the supplied string does not appear to be a FITS standard representation * of a complex value. * * @since 1.20 */ public Float(String str) throws IllegalArgumentException { super(str); } @Override Object toArray() { return new float[] {(float) re(), (float) im()}; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy