001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2021, 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 */
030package tech.units.indriya.internal.function.radix;
031
032import static org.apiguardian.api.API.Status.INTERNAL;
033
034import java.util.function.Consumer;
035
036import org.apiguardian.api.API;
037
038import tech.units.indriya.function.MixedRadix;
039import tech.units.indriya.internal.function.Calculator;
040
041/**
042 * Internal utility class to support {@link MixedRadix}.
043 * 
044 * @author Andi Huber
045 * @since 2.0
046 */
047@API(status=INTERNAL)
048public class MixedRadixSupport {
049
050    private final Radix[] radices;
051
052    /**
053     * 
054     * @param radices - most significant first
055     */
056    public MixedRadixSupport(Radix[] radices) {
057        this.radices = radices;
058    }
059    
060    /**
061     * 
062     * @param trailingRadixValue
063     * @param numberVisitor - gets past over the extracted numbers in least significant first order
064     */
065    public void visitRadixNumbers(Number trailingRadixValue, Consumer<Number> numberVisitor) {
066        
067        Number total = trailingRadixValue;
068        
069        for(int i=0;i<radices.length;++i) {
070            
071            Radix radix = radices[invertIndex(i)];
072            
073            boolean fractionalRemainder = i==0;
074            
075            Number[] divideAndRemainder = radix.divideAndRemainder(total, !fractionalRemainder); 
076            
077            Number remainder = divideAndRemainder[1];
078            
079            numberVisitor.accept(remainder);
080
081            total = divideAndRemainder[0];
082            
083        }
084        
085        numberVisitor.accept(total);
086        
087    }
088
089    /**
090     * @param values - numbers corresponding to the radices in most significant first order, 
091     *      allowed to be of shorter length than the total count of radices
092     * @return sum of {@code values} each converted to the 'scale' of the trailing radix (the least significant), 
093     *      as given by the constructor of this instance
094     */
095    public Number sumMostSignificant(Number[] values) {
096
097        int maxAllowedValueIndex = values.length - 1; 
098        
099        Number sum = values[0];
100        
101        for(int i=0;i<radices.length;++i) {
102            
103            sum = radices[i].multiply(sum);
104            
105            if(i >= maxAllowedValueIndex) {
106                continue; 
107            }
108            
109            sum = Calculator.of(sum).add(values[i+1]).peek(); // narrow each addition step
110            
111        }
112        
113        return sum;
114        
115    }
116    
117    // -- HELPER
118    
119    private int invertIndex(int index) {
120        return radices.length - index - 1;
121    }
122
123}