001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.metapath.item.atomic; 028 029import gov.nist.secauto.metaschema.core.metapath.function.ArithmeticFunctionException; 030import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; 031import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; 032import gov.nist.secauto.metaschema.core.util.ObjectUtils; 033 034import java.math.BigDecimal; 035import java.math.BigInteger; 036import java.math.MathContext; 037import java.math.RoundingMode; 038 039import edu.umd.cs.findbugs.annotations.NonNull; 040 041public interface INumericItem extends IAnyAtomicItem { 042 043 @NonNull 044 static INumericItem cast(@NonNull IAnyAtomicItem item) throws InvalidValueForCastFunctionException { 045 INumericItem retval; 046 if (item instanceof INumericItem) { 047 retval = (INumericItem) item; 048 } else { 049 try { 050 retval = IDecimalItem.valueOf(item.asString()); 051 } catch (NumberFormatException ex) { 052 throw new InvalidValueForCastFunctionException(ex); 053 } 054 } 055 return retval; 056 } 057 058 @NonNull 059 BigDecimal asDecimal(); 060 061 @NonNull 062 BigInteger asInteger(); 063 064 boolean toEffectiveBoolean(); 065 066 /** 067 * Get the absolute value of the item. 068 * 069 * @return this item negated if this item is negative, or the item otherwise 070 */ 071 @NonNull 072 INumericItem abs(); 073 074 /** 075 * Round the value to the whole number closest to positive infinity. 076 * 077 * @return the rounded value 078 */ 079 @NonNull 080 IIntegerItem ceiling(); 081 082 /** 083 * Round the value to the whole number closest to negative infinity. 084 * 085 * @return the rounded value 086 */ 087 @NonNull 088 IIntegerItem floor(); 089 090 @NonNull 091 default INumericItem round() { 092 return round(IIntegerItem.ZERO); 093 } 094 095 @NonNull 096 default INumericItem round(@NonNull IIntegerItem precisionItem) { 097 int precision; 098 try { 099 precision = FunctionUtils.asInteger(precisionItem); 100 } catch (ArithmeticException ex) { 101 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, 102 "Numeric operation overflow/underflow.", ex); 103 } 104 INumericItem retval; 105 if (precision >= 0) { 106 // round to precision decimal places 107 if (this instanceof IIntegerItem) { 108 retval = this; 109 } else { 110 // IDecimalItem 111 BigDecimal value = this.asDecimal(); 112 if (value.signum() == -1) { 113 retval = IDecimalItem.valueOf( 114 ObjectUtils.notNull( 115 value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_DOWN)))); 116 } else { 117 retval = IDecimalItem.valueOf( 118 ObjectUtils.notNull( 119 value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_UP)))); 120 } 121 } 122 } else { 123 // round to a power of 10 124 BigInteger value = this.asInteger(); 125 BigInteger divisor = BigInteger.TEN.pow(0 - precision); 126 127 @NonNull BigInteger result; 128 if (divisor.compareTo(value.abs()) > 0) { 129 result = ObjectUtils.notNull(BigInteger.ZERO); 130 } else { 131 BigInteger remainder = value.mod(divisor); 132 BigInteger lessRemainder = value.subtract(remainder); 133 BigInteger halfDivisor = divisor.divide(BigInteger.TWO); 134 result = ObjectUtils.notNull( 135 remainder.compareTo(halfDivisor) >= 0 ? lessRemainder.add(divisor) : lessRemainder); 136 } 137 retval = IIntegerItem.valueOf(result); 138 } 139 return retval; 140 } 141}