<commit_before>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Pchp.Core
{
    [DebuggerDisplay("{GetDebuggerValue,nq}", Type = "{GetDebuggerType,nq}")]
    [StructLayout(LayoutKind.Explicit)]
    public struct PhpNumber : IComparable<PhpNumber>, IPhpConvertible
    {
        #region Fields

        /// <summary>
        /// Number type.
        /// Valid values are <see cref="PhpTypeCode.Long"/> and <see cref="PhpTypeCode.Double"/>.
        /// </summary>
        [FieldOffset(0)]
        PhpTypeCode _typeCode;

        [FieldOffset(4)]
        long _long;

        [FieldOffset(4)]
        double _double;

        #endregion

        #region Properties

        /// <summary>
        /// Gets value indicating the number is a floating point number.
        /// </summary>
        public bool IsDouble { get { return _typeCode == PhpTypeCode.Double; } }

        /// <summary>
        /// Gets value indicating the number is an integer.
        /// </summary>
        public bool IsLong { get { return _typeCode == PhpTypeCode.Long; } }

        /// <summary>
        /// Gets the long field of the number.
        /// Does not perform a conversion, expects the number is of type long.
        /// </summary>
        public long Long
        {
            get
            {
                Debug.Assert(IsLong);
                return _long;
            }
        }

        /// <summary>
        /// Gets the double field of the number.
        /// Does not perform a conversion, expects the number is of type double.
        /// </summary>
        public double Double
        {
            get
            {
                Debug.Assert(IsDouble);
                return _double;
            }
        }

        #endregion

        #region Debug

        string GetDebuggerValue { get { return IsLong ? _long.ToString() : _double.ToString(); } }
        string GetDebuggerType { get { return IsLong ? PhpVariable.TypeNameInteger : PhpVariable.TypeNameDouble; } }

        /// <summary>
        /// Checks the number type code is valid.
        /// </summary>
        void AssertTypeCode()
        {
            Debug.Assert(_typeCode == PhpTypeCode.Long || _typeCode == PhpTypeCode.Double);
        }

        #endregion

        #region Operators

        #region Equality, Comparison

        /// <summary>
        /// Gets non strict comparison with another number.
        /// </summary>
        public int CompareTo(PhpNumber other)
        {
            this.AssertTypeCode();
            other.AssertTypeCode();

            //
            if (_typeCode == other._typeCode)
            {
                return (_typeCode == PhpTypeCode.Long)
                    ? _long.CompareTo(other._long)
                    : _double.CompareTo(other._double);
            }
            else
            {
                return (_typeCode == PhpTypeCode.Long)
                    ? ((double)_long).CompareTo(other._double)
                    : _double.CompareTo((double)other._long);
            }
        }

        /// <summary>
        /// Gets non strict comparison with another number.
        /// </summary>
        public int CompareTo(double dx)
        {
            return ToDouble().CompareTo(dx);
        }

        /// <summary>
        /// Gets non strict comparison with another number.
        /// </summary>
        public int CompareTo(long lx)
        {
            this.AssertTypeCode();

            return IsLong ? _long.CompareTo(lx) : _double.CompareTo((double)lx);
        }

        public static bool operator <(PhpNumber x, PhpNumber y)
        {
            return x.CompareTo(y) < 0;
        }

        public static bool operator >(PhpNumber x, PhpNumber y)
        {
            return x.CompareTo(y) > 0;
        }

        public static bool operator <(PhpNumber x, double dy)
        {
            return x.ToDouble() < dy;
        }

        public static bool operator >(PhpNumber x, double dy)
        {
            return x.ToDouble() > dy;
        }

        public static bool operator <(PhpNumber x, long ly)
        {
            x.AssertTypeCode();

            //
            return x.IsLong ? (x._long < ly) : (x._double < (double)ly);
        }

        public static bool operator >(PhpNumber x, long ly)
        {
            x.AssertTypeCode();

            //
            return x.IsLong ? (x._long > ly) : (x._double > (double)ly);
        }

        /// <summary>
        /// Non strict equality operator.
        /// </summary>
        public static bool operator ==(PhpNumber a, PhpNumber b)
        {
            a.AssertTypeCode();
            b.AssertTypeCode();

            //
            if (a._typeCode == b._typeCode)
            {
                return (a._typeCode == PhpTypeCode.Long)
                    ? a._long == b._long
                    : a._double == b._double;
            }
            else
            {
                return (a._typeCode == PhpTypeCode.Long)
                    ? (double)a._long == b._double
                    : a._double == (double)b._long;
            }
        }

        /// <summary>
        /// Non strict inequality operator.
        /// </summary>
        public static bool operator !=(PhpNumber a, PhpNumber b)
        {
            return !(a == b);
        }

        #endregion

        #region Add

        /// <summary>
        /// Implements <c>+</c> operator on numbers.
        /// </summary>
        /// <param name="x">First operand.</param>
        /// <param name="y">Second operand.</param>
        /// <returns></returns>
        public static PhpNumber operator +(PhpNumber x, PhpNumber y)
        {
            // at least one operand is a double:
            if (x.IsDouble)
            {
                // DOUBLE + DOUBLE|LONG<commit_msg>specialized operator for number == value<commit_after>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Pchp.Core
{
    [DebuggerDisplay("{GetDebuggerValue,nq}", Type = "{GetDebuggerType,nq}")]
    [StructLayout(LayoutKind.Explicit)]
    public struct PhpNumber : IComparable<PhpNumber>, IPhpConvertible
    {
        #region Fields

        /// <summary>
        /// Number type.
        /// Valid values are <see cref="PhpTypeCode.Long"/> and <see cref="PhpTypeCode.Double"/>.
        /// </summary>
        [FieldOffset(0)]
        PhpTypeCode _typeCode;

        [FieldOffset(4)]
        long _long;

        [FieldOffset(4)]
        double _double;

        #endregion

        #region Properties

        /// <summary>
        /// Gets value indicating the number is a floating point number.
        /// </summary>
        public bool IsDouble { get { return _typeCode == PhpTypeCode.Double; } }

        /// <summary>
        /// Gets value indicating the number is an integer.
        /// </summary>
        public bool IsLong { get { return _typeCode == PhpTypeCode.Long; } }

        /// <summary>
        /// Gets the long field of the number.
        /// Does not perform a conversion, expects the number is of type long.
        /// </summary>
        public long Long
        {
            get
            {
                Debug.Assert(IsLong);
                return _long;
            }
        }

        /// <summary>
        /// Gets the double field of the number.
        /// Does not perform a conversion, expects the number is of type double.
        /// </summary>
        public double Double
        {
            get
            {
                Debug.Assert(IsDouble);
                return _double;
            }
        }

        #endregion

        #region Debug

        string GetDebuggerValue { get { return IsLong ? _long.ToString() : _double.ToString(); } }
        string GetDebuggerType { get { return IsLong ? PhpVariable.TypeNameInteger : PhpVariable.TypeNameDouble; } }

        /// <summary>
        /// Checks the number type code is valid.
        /// </summary>
        void AssertTypeCode()
        {
            Debug.Assert(_typeCode == PhpTypeCode.Long || _typeCode == PhpTypeCode.Double);
        }

        #endregion

        #region Operators

        #region Equality, Comparison

        /// <summary>
        /// Gets non strict comparison with another number.
        /// </summary>
        public int CompareTo(PhpNumber other)
        {
            this.AssertTypeCode();
            other.AssertTypeCode();

            //
            if (_typeCode == other._typeCode)
            {
                return (_typeCode == PhpTypeCode.Long)
                    ? _long.CompareTo(other._long)
                    : _double.CompareTo(other._double);
            }
            else
            {
                return (_typeCode == PhpTypeCode.Long)
                    ? ((double)_long).CompareTo(other._double)
                    : _double.CompareTo((double)other._long);
            }
        }

        /// <summary>
        /// Gets non strict comparison with another number.
        /// </summary>
        public int CompareTo(double dx)
        {
            return ToDouble().CompareTo(dx);
        }

        /// <summary>
        /// Gets non strict comparison with another number.
        /// </summary>
        public int CompareTo(long lx)
        {
            this.AssertTypeCode();

            return IsLong ? _long.CompareTo(lx) : _double.CompareTo((double)lx);
        }

        public static bool operator <(PhpNumber x, PhpNumber y)
        {
            return x.CompareTo(y) < 0;
        }

        public static bool operator >(PhpNumber x, PhpNumber y)
        {
            return x.CompareTo(y) > 0;
        }

        public static bool operator <(PhpNumber x, double dy)
        {
            return x.ToDouble() < dy;
        }

        public static bool operator >(PhpNumber x, double dy)
        {
            return x.ToDouble() > dy;
        }

        public static bool operator <(PhpNumber x, long ly)
        {
            x.AssertTypeCode();

            //
            return x.IsLong ? (x._long < ly) : (x._double < (double)ly);
        }

        public static bool operator >(PhpNumber x, long ly)
        {
            x.AssertTypeCode();

            //
            return x.IsLong ? (x._long > ly) : (x._double > (double)ly);
        }

        /// <summary>
        /// Non strict equality operator.
        /// </summary>
        public static bool operator ==(PhpNumber a, PhpNumber b)
        {
            a.AssertTypeCode();
            b.AssertTypeCode();

            //
            if (a._typeCode == b._typeCode)
            {
                return (a._typeCode == PhpTypeCode.Long)
                    ? a._long == b._long
                    : a._double == b._double;
            }
            else
            {
                return (a._typeCode == PhpTypeCode.Long)
                    ? (double)a._long == b._double
                    : a._double == (double)b._long;
            }
        }

        /// <summary>
        /// Non strict inequality operator.
        /// </summary>
        public static bool operator !=(PhpNumber a, PhpNumber b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Non strict equality operator.
        /// </summary>
        public static bool operator ==(PhpNumber x, long y)
            => x.IsLong ? x.Long == y : x.Double == y;

        /// <summary>
        /// Non strict equality operator.
        /// </summary>
        public static bool operator !=(PhpNumber x, long y)
            => !(x == y);

        /// <summary>
        /// Non strict equality operator.
        /// </summary>
        public static bool operator ==(PhpNumber x, double y)
            => x.ToDouble() == y;

        /// <summary>
        /// Non strict equality operator.
        /// </summary>
        public static bool operator !=(PhpNumber x, double y)
            => !(x == y);

        #endregion

        #region Add

        /// <summary>
        /// Implements <c>+</c> operator on numbers.
        /// </summary>
        /// <param name="x">First operand.</param>
        /// <param name="y">Second operand.</param>
        /// <returns></returns>
        public static PhpNumber operator +(PhpNumber x, PhpNumber y)
        {
            // at least one operand is a double:
            if (x.IsDouble)
            {
                // DOUBLE + DOUBLE|LONG
{'fraction_non_alphanumeric': 0.11595812182741116, 'fraction_numerical': 0.0007931472081218274, 'mean_word_length': 1.3621206444361185, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 99, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3200996', 'n_tokens_mistral': 3392, 'n_tokens_neox': 3299, 'n_words': 878}