File: PrimitiveColumnContainer.BinaryOperations.cs
Web Access
Project: src\src\Microsoft.Data.Analysis\Microsoft.Data.Analysis.csproj (Microsoft.Data.Analysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
 
namespace Microsoft.Data.Analysis
{
    internal partial class PrimitiveColumnContainer<T>
        where T : unmanaged
    {
        public PrimitiveColumnContainer<T> HandleOperation(BinaryOperation operation, PrimitiveColumnContainer<T> right)
        {
            var arithmetic = Arithmetic<T>.Instance;
 
            //Divisions are special cases
            var specialCase = (operation == BinaryOperation.Divide || operation == BinaryOperation.Modulo);
 
            long nullCount = specialCase ? NullCount : 0;
            for (int i = 0; i < this.Buffers.Count; i++)
            {
                var mutableBuffer = this.Buffers.GetOrCreateMutable(i);
                var leftSpan = mutableBuffer.Span;
                var rightSpan = right.Buffers[i].ReadOnlySpan;
 
                var leftValidity = this.NullBitMapBuffers.GetOrCreateMutable(i).Span;
                var rightValidity = right.NullBitMapBuffers[i].ReadOnlySpan;
 
                if (specialCase)
                {
                    for (var j = 0; j < leftSpan.Length; j++)
                    {
                        if (BitUtility.GetBit(rightValidity, j))
                            leftSpan[j] = arithmetic.HandleOperation(operation, leftSpan[j], rightSpan[j]);
                        else if (BitUtility.GetBit(leftValidity, j))
                        {
                            BitUtility.ClearBit(leftValidity, j);
 
                            //Increase NullCount
                            nullCount++;
                        }
                    }
                }
                else
                {
                    arithmetic.HandleOperation(operation, leftSpan, rightSpan, leftSpan);
                    ValidityElementwiseAnd(leftValidity, rightValidity, leftValidity);
 
                    //Calculate NullCount
                    nullCount += mutableBuffer.Length - BitUtility.GetBitCount(leftValidity, mutableBuffer.Length);
                }
            }
 
            NullCount = nullCount;
            return this;
        }
 
        public PrimitiveColumnContainer<T> HandleOperation(BinaryOperation operation, T right)
        {
            var arithmetic = Arithmetic<T>.Instance;
            for (int i = 0; i < this.Buffers.Count; i++)
            {
                var leftSpan = this.Buffers.GetOrCreateMutable(i).Span;
 
                arithmetic.HandleOperation(operation, leftSpan, right, leftSpan);
            }
 
            return this;
        }
 
        public PrimitiveColumnContainer<T> HandleReverseOperation(BinaryOperation operation, T left)
        {
            var arithmetic = Arithmetic<T>.Instance;
            for (int i = 0; i < this.Buffers.Count; i++)
            {
                var rightSpan = this.Buffers.GetOrCreateMutable(i).Span;
                var rightValidity = this.NullBitMapBuffers[i].ReadOnlySpan;
 
                if (operation == BinaryOperation.Divide || operation == BinaryOperation.Modulo)
                {
                    //Divisions are special cases
                    for (var j = 0; j < rightSpan.Length; j++)
                    {
                        if (BitUtility.GetBit(rightValidity, j))
                            rightSpan[j] = arithmetic.HandleOperation(operation, left, rightSpan[j]);
                    }
                }
                else
                    arithmetic.HandleOperation(operation, left, rightSpan, rightSpan);
            }
 
            return this;
        }
 
        public PrimitiveColumnContainer<T> HandleOperation(BinaryIntOperation operation, int right)
        {
            var arithmetic = Arithmetic<T>.Instance;
            for (int i = 0; i < this.Buffers.Count; i++)
            {
                var leftSpan = this.Buffers.GetOrCreateMutable(i).Span;
 
                arithmetic.HandleOperation(operation, leftSpan, right, leftSpan);
            }
 
            return this;
        }
 
        public PrimitiveColumnContainer<bool> HandleOperation(ComparisonOperation operation, PrimitiveColumnContainer<T> right)
        {
            var ret = new PrimitiveColumnContainer<bool>(Length, false);
            var arithmetic = Arithmetic<T>.Instance;
 
            //Size of any buffer in PrimitiveColumnContainer<bool> is larger (or equal) than size of the buffers for other types
            long index = 0;
            for (int i = 0; i < this.Buffers.Count; i++)
            {
                var leftSpan = this.Buffers[i].ReadOnlySpan;
                var rightSpan = right.Buffers[i].ReadOnlySpan;
 
                // Get correct ret Span for storing results
                var retSpanIndex = ret.GetIndexOfBufferContainingRowIndex(index);
                var retSpan = ret.Buffers.GetOrCreateMutable(retSpanIndex).Span;
 
                //Get offset in the buffer to store new data
                var retOffset = (int)(index % DataFrameBuffer<bool>.MaxCapacity);
 
                //Check if there is enought space in the current ret buffer
                var availableInRetSpan = DataFrameBuffer<bool>.MaxCapacity - retOffset;
                if (availableInRetSpan < leftSpan.Length)
                {
                    //We are not able to place all results into remaining space into our ret buffer, have to split the results
 
                    //This will be simplified when the size of buffers of different types are done equal
                    //(not supported by classic .Net framework due to the 2 Gb limitation on array size)
 
                    arithmetic.HandleOperation(operation, leftSpan.Slice(0, availableInRetSpan), rightSpan.Slice(0, availableInRetSpan), retSpan.Slice(retOffset));
 
                    var nextRetSpan = ret.Buffers.GetOrCreateMutable(retSpanIndex + 1).Span;
                    arithmetic.HandleOperation(operation, leftSpan.Slice(availableInRetSpan), rightSpan.Slice(availableInRetSpan), nextRetSpan);
                }
                else
                    arithmetic.HandleOperation(operation, leftSpan, rightSpan, retSpan.Slice(retOffset));
 
                index += leftSpan.Length;
            }
 
            return ret;
        }
 
        public PrimitiveColumnContainer<bool> HandleOperation(ComparisonOperation operation, T right)
        {
            var ret = new PrimitiveColumnContainer<bool>(Length, false);
            var arithmetic = Arithmetic<T>.Instance;
 
            //Size of any buffer in PrimitiveColumnContainer<bool> is larger (or equal) than size of the buffers for other types
            long index = 0;
            for (int i = 0; i < this.Buffers.Count; i++)
            {
                var leftSpan = this.Buffers[i].ReadOnlySpan;
 
                //Get correct ret Span for storing results
                var retSpanIndex = ret.GetIndexOfBufferContainingRowIndex(index);
                var retSpan = ret.Buffers.GetOrCreateMutable(retSpanIndex).Span;
 
                //Get offset in the buffer to store new data
                var retOffset = (int)(index % DataFrameBuffer<bool>.MaxCapacity);
 
                //Check if there is enought space in the current ret buffer
                var availableInRetSpan = DataFrameBuffer<bool>.MaxCapacity - retOffset;
 
                if (availableInRetSpan < leftSpan.Length)
                {
                    //We are not able to place all results into remaining space into our ret buffer, have to split the results
 
                    //This will be simplified when the size of buffers of different types are done equal
                    //(not supported by classic .Net framework due to the 2 Gb limitation on array size)
 
                    arithmetic.HandleOperation(operation, leftSpan.Slice(0, availableInRetSpan), right, retSpan.Slice(retOffset));
 
                    var nextRetSpan = ret.Buffers.GetOrCreateMutable(retSpanIndex + 1).Span;
                    arithmetic.HandleOperation(operation, leftSpan.Slice(availableInRetSpan), right, nextRetSpan);
                }
                else
                    arithmetic.HandleOperation(operation, leftSpan, right, retSpan.Slice(retOffset));
 
                index += leftSpan.Length;
            }
 
            return ret;
        }
 
        private static void ValidityElementwiseAnd(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right, Span<byte> destination)
        {
            for (var i = 0; i < left.Length; i++)
                destination[i] = (byte)(left[i] & right[i]);
        }
    }
}