001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031package tech.units.indriya.internal.simplify; 032 033import java.util.HashMap; 034import java.util.Map; 035import java.util.Objects; 036import java.util.function.BiPredicate; 037import java.util.function.BinaryOperator; 038 039import tech.units.indriya.AbstractConverter; 040import tech.units.indriya.function.AddConverter; 041import tech.units.indriya.function.ExpConverter; 042import tech.units.indriya.function.LogConverter; 043import tech.units.indriya.function.MultiplyConverter; 044import tech.units.indriya.function.PowerOfIntConverter; 045import tech.units.indriya.function.PowerOfPiConverter; 046import tech.units.indriya.function.RationalConverter; 047 048/** 049 * Simplifier for UnitConverter composition yielding a normal-form. 050 * A normal-form is required to decide whether two UnitConverters are equivalent. 051 * 052 * @author Andi Huber 053 * @version 1.0 054 * @since 2.0 055 */ 056public final class Simplifier { 057 058 final static Map<Class<?>, Integer> normalFormOrder = new HashMap<>(6); 059 static { 060 normalFormOrder.put(AbstractConverter.IDENTITY.getClass(), 0); 061 normalFormOrder.put(PowerOfIntConverter.class, 1); 062 normalFormOrder.put(RationalConverter.class, 2); 063 normalFormOrder.put(PowerOfPiConverter.class, 3); 064 normalFormOrder.put(MultiplyConverter.class, 4); 065 normalFormOrder.put(AddConverter.class, 5); 066 normalFormOrder.put(LogConverter.class, 6); 067 normalFormOrder.put(ExpConverter.class, 7); 068 normalFormOrder.put(AbstractConverter.Pair.class, 99); 069 } 070 071 /** 072 * 073 * @param a 074 * @param b 075 * @param simpleComposeTest 076 * @param simpleComposeAction 077 * @return normal-form 078 */ 079 public static AbstractConverter compose( 080 AbstractConverter a, 081 AbstractConverter b, 082 BiPredicate<AbstractConverter, AbstractConverter> simpleComposeTest, 083 BinaryOperator<AbstractConverter> simpleComposeAction 084 ) { 085 086 if(a.isIdentity()) { 087 if(b.isIdentity()) { 088 return isNormalFormOrderWhenIdentity(a, b) ? a : b; 089 } 090 return b; 091 } 092 if(b.isIdentity()) { 093 return a; 094 } 095 096 if(simpleComposeTest.test(a, b)) { 097 return simpleComposeAction.apply(a, b); 098 } 099 100 final boolean commutative = a.isLinear() && b.isLinear(); 101 final boolean swap = commutative && !isNormalFormOrderWhenCommutative(a, b); 102 103 final AbstractConverter.Pair nonSimplifiedForm = swap 104 ? new AbstractConverter.Pair(b, a) 105 : new AbstractConverter.Pair(a, b); 106 107 return new SimplificationWorker( 108 simpleComposeTest, 109 simpleComposeAction ) 110 .simplify(nonSimplifiedForm.getConversionSteps()); 111 } 112 113 114 static boolean isNormalFormOrderWhenIdentity(AbstractConverter a, AbstractConverter b) { 115 if(a.getClass().equals(b.getClass())) { 116 return true; 117 } 118 return normalFormOrder.get(a.getClass()) <= normalFormOrder.get(b.getClass()); 119 } 120 121 static boolean isNormalFormOrderWhenCommutative(AbstractConverter a, AbstractConverter b) { 122 if(a.getClass().equals(b.getClass())) { 123 if(a instanceof PowerOfIntConverter) { 124 return ((PowerOfIntConverter)a).getBase() <= ((PowerOfIntConverter)b).getBase(); 125 } 126// if(a instanceof LogConverter) { 127// return ((LogConverter)a).getBase() <= ((LogConverter)b).getBase(); 128// } 129// if(a instanceof ExpConverter) { 130// return ((ExpConverter)a).getBase() <= ((ExpConverter)b).getBase(); 131// } 132 return true; 133 } 134 135 Integer orderA = Objects.requireNonNull(normalFormOrder.get(a.getClass()), 136 ()->String.format("no normal-form order defined for class '%s'", a.getClass().getName())); 137 Integer orderB = Objects.requireNonNull(normalFormOrder.get(b.getClass()), 138 ()->String.format("no normal-form order defined for class '%s'", b.getClass().getName())); 139 140 return orderA <= orderB; 141 } 142}