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