using System;

namespace Lucene.Net.Util
{
    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    /// <summary>
    /// Floating point numbers smaller than 32 bits.
    /// <para/>
    /// NOTE: This was SmallFloat in Lucene
    ///
    /// @lucene.internal
    /// </summary>
    public class SmallSingle
    {
        /// <summary>
        /// No instance </summary>
        private SmallSingle()
        {
        }

        /// <summary>
        /// Converts a 32 bit <see cref="float"/> to an 8 bit <see cref="float"/>.
        /// <br>Values less than zero are all mapped to zero.
        /// <br>Values are truncated (rounded down) to the nearest 8 bit value.
        /// <br>Values between zero and the smallest representable value
        /// are rounded up.
        /// </summary>
        /// <param name="f"> the 32 bit <see cref="float"/> to be converted to an 8 bit <see cref="float"/> (<see cref="byte"/>)  </param>
        /// <param name="numMantissaBits"> the number of mantissa bits to use in the byte, with the remainder to be used in the exponent </param>
        /// <param name="zeroExp"> the zero-point in the range of exponent values </param>
        /// <returns> the 8 bit float representation </returns>
        // LUCENENET specific overload for CLS compliance
        public static byte SingleToByte(float f, int numMantissaBits, int zeroExp)
        {
            return (byte)SingleToSByte(f, numMantissaBits, zeroExp);
        }

        /// <summary>
        /// Converts a 32 bit <see cref="float"/> to an 8 bit <see cref="float"/>.
        /// <para/>Values less than zero are all mapped to zero.
        /// <para/>Values are truncated (rounded down) to the nearest 8 bit value.
        /// <para/>Values between zero and the smallest representable value
        /// are rounded up.
        /// <para/>
        /// NOTE: This was floatToByte() in Lucene
        /// </summary>
        /// <param name="f"> the 32 bit <see cref="float"/> to be converted to an 8 bit <see cref="float"/> (<see cref="sbyte"/>) </param>
        /// <param name="numMantissaBits"> the number of mantissa bits to use in the byte, with the remainder to be used in the exponent </param>
        /// <param name="zeroExp"> the zero-point in the range of exponent values </param>
        /// <returns> the 8 bit float representation </returns>
        [CLSCompliant(false)]
        public static sbyte SingleToSByte(float f, int numMantissaBits, int zeroExp)
        {
            // Adjustment from a float zero exponent to our zero exponent,
            // shifted over to our exponent position.
            int fzero = (63 - zeroExp) << numMantissaBits;
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(f), 0);
            int smallfloat = bits >> (24 - numMantissaBits);
            if (smallfloat <= fzero)
            {
                return (bits <= 0) ? (sbyte)0 : (sbyte)1; // underflow is mapped to smallest non-zero number. -  negative numbers and zero both map to 0 byte
            }
            else if (smallfloat >= fzero + 0x100)
            {
                return -1; // overflow maps to largest number
            }
            else
            {
                return (sbyte)(smallfloat - fzero);
            }
        }

        /// <summary>
        /// Converts an 8 bit <see cref="float"/> to a 32 bit <see cref="float"/>. 
        /// <para/>
        /// NOTE: This was byteToFloat() in Lucene
        /// </summary>
        // LUCENENET specific overload for CLS compliance
        public static float ByteToSingle(byte b, int numMantissaBits, int zeroExp)
        {
            return SByteToSingle((sbyte)b, numMantissaBits, zeroExp);
        }

        /// <summary>
        /// Converts an 8 bit <see cref="float"/> to a 32 bit <see cref="float"/>. 
        /// <para/>
        /// NOTE: This was byteToFloat() in Lucene
        /// </summary>
        [CLSCompliant(false)]
        public static float SByteToSingle(sbyte b, int numMantissaBits, int zeroExp)
        {
            // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
            // is only a little bit faster (anywhere from 0% to 7%)
            if (b == 0)
            {
                return 0.0f;
            }
            int bits = (b & 0xff) << (24 - numMantissaBits);
            bits += (63 - zeroExp) << 24;
            return BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
        }

        //
        // Some specializations of the generic functions follow.
        // The generic functions are just as fast with current (1.5)
        // -server JVMs, but still slower with client JVMs.
        //

        /// <summary>
        /// SingleToSByte((byte)b, mantissaBits=3, zeroExponent=15)
        /// <para/>smallest non-zero value = 5.820766E-10
        /// <para/>largest value = 7.5161928E9
        /// <para/>epsilon = 0.125
        /// <para/>
        /// NOTE: This was floatToByte315() in Lucene
        /// </summary>
        // LUCENENET specific overload for CLS compliance
        public static byte SingleToByte315(float f)
        {
            return (byte)SingleToSByte315(f);
        }

        /// <summary>
        /// SingleToSByte(b, mantissaBits=3, zeroExponent=15)
        /// <para/>smallest non-zero value = 5.820766E-10
        /// <para/>largest value = 7.5161928E9
        /// <para/>epsilon = 0.125
        /// <para/>
        /// NOTE: This was floatToByte315() in Lucene
        /// </summary>
        [CLSCompliant(false)]
        public static sbyte SingleToSByte315(float f) 
        {
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(f), 0);
            int smallfloat = bits >> (24 - 3);
            if (smallfloat <= ((63 - 15) << 3))
            {
                return (bits <= 0) ? (sbyte)0 : (sbyte)1;
            }
            if (smallfloat >= ((63 - 15) << 3) + 0x100)
            {
                return -1;
            }
            return (sbyte)(smallfloat - ((63 - 15) << 3));
        }

        /// <summary>
        /// ByteToSingle(b, mantissaBits=3, zeroExponent=15) 
        /// <para/>
        /// NOTE: This was byte315ToFloat() in Lucene
        /// </summary>
        // LUCENENET specific overload for CLS compliance
        public static float Byte315ToSingle(byte b)
        {
            return SByte315ToSingle((sbyte)b);
        }

        /// <summary>
        /// SByteToSingle(b, mantissaBits=3, zeroExponent=15) 
        /// <para/>
        /// NOTE: This was byte315ToFloat() in Lucene
        /// </summary>
        [CLSCompliant(false)]
        public static float SByte315ToSingle(sbyte b)
        {
            // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
            // is only a little bit faster (anywhere from 0% to 7%)
            if (b == 0)
            {
                return 0.0f;
            }
            int bits = (b & 0xff) << (24 - 3);
            bits += (63 - 15) << 24;
            return BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
        }

        /// <summary>
        /// SingleToByte(b, mantissaBits=5, zeroExponent=2)
        /// <para/>smallest nonzero value = 0.033203125
        /// <para/>largest value = 1984.0
        /// <para/>epsilon = 0.03125
        /// <para/>
        /// NOTE: This was floatToByte52() in Lucene
        /// </summary>
        // LUCENENET specific overload for CLS compliance
        public static byte SingleToByte52(float f)
        {
            return (byte)SingleToSByte315(f);
        }

        /// <summary>
        /// SingleToSByte(b, mantissaBits=5, zeroExponent=2)
        /// <para/>smallest nonzero value = 0.033203125
        /// <para/>largest value = 1984.0
        /// <para/>epsilon = 0.03125
        /// <para/>
        /// NOTE: This was floatToByte52() in Lucene
        /// </summary>
        [CLSCompliant(false)]
        public static sbyte SingleToSByte52(float f)
        {
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(f), 0);
            int smallfloat = bits >> (24 - 5);
            if (smallfloat <= (63 - 2) << 5)
            {
                return (bits <= 0) ? (sbyte)0 : (sbyte)1;
            }
            if (smallfloat >= ((63 - 2) << 5) + 0x100)
            {
                return -1;
            }
            return (sbyte)(smallfloat - ((63 - 2) << 5));
        }

        /// <summary>
        /// ByteToFloat(b, mantissaBits=5, zeroExponent=2) 
        /// <para/>
        /// NOTE: This was byte52ToFloat() in Lucene
        /// </summary>
        // LUCENENET specific overload for CLS compliance
        public static float Byte52ToSingle(byte b)
        {
            return SByte52ToSingle((sbyte)b);
        }

        /// <summary>
        /// SByteToFloat(b, mantissaBits=5, zeroExponent=2) 
        /// <para/>
        /// NOTE: This was byte52ToFloat() in Lucene
        /// </summary>
        [CLSCompliant(false)]
        public static float SByte52ToSingle(sbyte b)
        {
            // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
            // is only a little bit faster (anywhere from 0% to 7%)
            if (b == 0)
            {
                return 0.0f;
            }
            int bits = (b & 0xff) << (24 - 5);
            bits += (63 - 2) << 24;
            return BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
        }
    }
}