/* Copyright 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed 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.
 */

/** \addtogroup ecma ECMA
 * @{
 *
 * \addtogroup ecmahelpers Helpers for operations with ECMA data types
 * @{
 */

#include "ecma-globals.h"
#include "ecma-helpers.h"

#ifdef CONFIG_ECMA_NUMBER_FLOAT32
JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint32_t));

/**
 * Width of sign field
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
#define ECMA_NUMBER_SIGN_WIDTH       (1)

/**
 * Width of biased exponent field
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
#define ECMA_NUMBER_BIASED_EXP_WIDTH (8)

/**
 * Width of fraction field
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
#define ECMA_NUMBER_FRACTION_WIDTH   (23)

/**
 * Field of ecma-number
 *
 * See also:
 *          IEEE-754 2008, 3.4
 */
typedef struct
{
  /** fraction field */
  unsigned int fraction : ECMA_NUMBER_FRACTION_WIDTH;

  /** biased exponent field */
  unsigned int biased_exp : ECMA_NUMBER_BIASED_EXP_WIDTH;

  /** sign bit */
  unsigned int sign : ECMA_NUMBER_SIGN_WIDTH;
} ecma_number_fields_t;

/**
 * Value used to calculate exponent from biased exponent
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
const int32_t ecma_number_exponent_bias = 127;
#elif defined (CONFIG_ECMA_NUMBER_FLOAT64)
JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint64_t));

/**
 * Width of sign field
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
#define ECMA_NUMBER_SIGN_WIDTH       (1)

/**
 * Width of biased exponent field
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
#define ECMA_NUMBER_BIASED_EXP_WIDTH (11)

/**
 * Width of fraction field
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
#define ECMA_NUMBER_FRACTION_WIDTH   (52)

/**
 * Field of ecma-number
 *
 * See also:
 *          IEEE-754 2008, 3.4
 */
typedef struct
{
  /** fraction field */
  unsigned long int fraction : ECMA_NUMBER_FRACTION_WIDTH;

  /** biased exponent field */
  unsigned long int biased_exp : ECMA_NUMBER_BIASED_EXP_WIDTH;

  /** sign bit */
  unsigned long int sign : ECMA_NUMBER_SIGN_WIDTH;
} ecma_number_fields_t;

/**
 * Value used to calculate exponent from biased exponent
 *
 * See also:
 *          IEEE-754 2008, 3.6, Table 3.5
 */
const int32_t ecma_number_exponent_bias = 1023;
#else /* !CONFIG_ECMA_NUMBER_FLOAT32 && !CONFIG_ECMA_NUMBER_FLOAT64 */
# error "!CONFIG_ECMA_NUMBER_FLOAT32 && !CONFIG_ECMA_NUMBER_FLOAT64"
#endif /* !CONFIG_ECMA_NUMBER_FLOAT32 && !CONFIG_ECMA_NUMBER_FLOAT64 */

/**
 * Get fraction of number
 *
 * @return normalized fraction field of number
 */
static uint64_t
ecma_number_get_fraction_field (ecma_number_t num) /**< ecma-number */
{
  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  u.value = num;

  return u.fields.fraction;
} /* ecma_number_get_fraction_field */

/**
 * Get exponent of number
 *
 * @return exponent corresponding to normalized fraction of number
 */
static uint32_t
ecma_number_get_biased_exponent_field (ecma_number_t num) /**< ecma-number */
{
  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  u.value = num;

  return u.fields.biased_exp;
} /* ecma_number_get_biased_exponent_field */

/**
 * Get sign bit of number
 *
 * @return 0 or 1 - value of sign bit
 */
static uint32_t
ecma_number_get_sign_field (ecma_number_t num) /**< ecma-number */
{
  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  u.value = num;

  return u.fields.sign;
} /* ecma_number_get_sign_field */

/**
 * Check if ecma-number is NaN
 *
 * @return true - if biased exponent is filled with 1 bits and
                  fraction is filled with anything but not all zero bits,
 *         false - otherwise
 */
bool
ecma_number_is_nan (ecma_number_t num) /**< ecma-number */
{
  uint32_t biased_exp = ecma_number_get_biased_exponent_field (num);
  uint64_t fraction = ecma_number_get_fraction_field (num);

   /* IEEE-754 2008, 3.4, a */

  return ((biased_exp == (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1)
          && (fraction != 0));
} /* ecma_number_is_nan */

/**
 * Make a NaN.
 *
 * @return NaN value
 */
ecma_number_t
ecma_number_make_nan (void)
{
  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  u.fields.biased_exp = (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1;
  u.fields.fraction = 1;
  u.fields.sign = 0;

  return u.value;
} /* ecma_number_make_nan */

/**
 * Make an Infinity.
 *
 * @return if !sign - +Infinity value,
 *         else - -Infinity value.
 */
ecma_number_t
ecma_number_make_infinity (bool sign) /**< true - for negative Infinity,
                                           false - for positive Infinity */
{
  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  u.fields.biased_exp = (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1;
  u.fields.fraction = 0;
  u.fields.sign = sign;

  return u.value;
} /* ecma_number_make_infinity */

/**
 * Check if ecma-number is negative
 *
 * @return true - if sign bit of ecma-number is set
 *         false - otherwise
 */
bool
ecma_number_is_negative (ecma_number_t num) /**< ecma-number */
{
  JERRY_ASSERT (!ecma_number_is_nan (num));

  /* IEEE-754 2008, 3.4 */

  return (ecma_number_get_sign_field (num) != 0);
} /* ecma_number_is_negative */

/**
 * Check if ecma-number is zero
 *
 * @return true - if fraction is zero and biased exponent is zero,
 *         false - otherwise
 */
bool
ecma_number_is_zero (ecma_number_t num) /**< ecma-number */
{
  JERRY_ASSERT (!ecma_number_is_nan (num));

  /* IEEE-754 2008, 3.4, e */

  return (ecma_number_get_fraction_field (num) == 0
          && ecma_number_get_biased_exponent_field (num) == 0);
} /* ecma_number_is_zero */

/**
 * Check if number is infinity
 *
 * @return true - if biased exponent is filled with 1 bits and
 *                fraction is filled with zero bits,
 *         false - otherwise.
 */
bool
ecma_number_is_infinity (ecma_number_t num) /**< ecma-number */
{
  JERRY_ASSERT (!ecma_number_is_nan (num));

  uint32_t biased_exp = ecma_number_get_biased_exponent_field (num);
  uint64_t fraction = ecma_number_get_fraction_field (num);

  /* IEEE-754 2008, 3.4, b */

  return ((biased_exp  == (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1)
          && (fraction == 0));
} /* ecma_number_is_infinity */

/**
 * Get fraction and exponent of the number
 *
 * @return shift of dot in the fraction
 */
int32_t
ecma_number_get_fraction_and_exponent (ecma_number_t num, /**< ecma-number */
                                       uint64_t *out_fraction_p, /**< out: fraction of the number */
                                       int32_t *out_exponent_p) /**< out: exponent of the number */
{
  JERRY_ASSERT (!ecma_number_is_nan (num));
  JERRY_ASSERT (!ecma_number_is_zero (num));
  JERRY_ASSERT (!ecma_number_is_infinity (num));

  uint32_t biased_exp = ecma_number_get_biased_exponent_field (num);
  uint64_t fraction = ecma_number_get_fraction_field (num);
  int32_t exponent;

  if (unlikely (biased_exp == 0))
  {
    /* IEEE-754 2008, 3.4, d */
    exponent = 1 - ecma_number_exponent_bias;

    while (!(fraction & (1ul << ECMA_NUMBER_FRACTION_WIDTH)))
    {
      JERRY_ASSERT (fraction != 0);

      fraction <<= 1;
      exponent--;
    }
  }
  else
  {
    /* IEEE-754 2008, 3.4, c */
    exponent = (int32_t) biased_exp - ecma_number_exponent_bias;

    JERRY_ASSERT (biased_exp > 0 && biased_exp < (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1);
    JERRY_ASSERT ((fraction & (1ul << ECMA_NUMBER_FRACTION_WIDTH)) == 0);
    fraction |= 1ul << ECMA_NUMBER_FRACTION_WIDTH;
  }

  *out_fraction_p = fraction;
  *out_exponent_p = exponent;
  return ECMA_NUMBER_FRACTION_WIDTH;
} /* ecma_number_get_fraction_and_exponent */

/**
 * Make normalised positive Number from given fraction and exponent
 *
 * @return ecma-number
 */
ecma_number_t
ecma_number_make_normal_positive_from_fraction_and_exponent (uint64_t fraction, /**< fraction */
                                                             int32_t exponent) /**< exponent */
{
  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  uint32_t biased_exp = (uint32_t) (exponent + ecma_number_exponent_bias);
  JERRY_ASSERT (biased_exp > 0 && biased_exp < (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1);

  u.fields.biased_exp = biased_exp & ((1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1);
  u.fields.fraction = fraction & ((1u << ECMA_NUMBER_FRACTION_WIDTH) - 1);
  u.fields.sign = 0;

  return u.value;
} /* ecma_number_make_normal_positive_from_fraction_and_exponent */

/**
 * Negate ecma-number
 *
 * @return negated number
 */
ecma_number_t
ecma_number_negate (ecma_number_t num) /**< ecma-number */
{
  JERRY_ASSERT (!ecma_number_is_nan (num));

  union
  {
    ecma_number_fields_t fields;
    ecma_number_t value;
  } u;

  u.value = num;

  u.fields.sign = !u.fields.sign;

  return u.value;
} /* ecma_number_negate */


/**
 * @}
 * @}
 */
