/****************************************************************************
 * apps/interpreters/bas/bas_value.c
 *
 *   Copyright (c) 1999-2014 Michael Haardt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Adapted to NuttX and re-released under a 3-clause BSD license:
 *
 *   Copyright (C) 2014 Gregory Nutt. All rights reserved.
 *   Authors: Alan Carvalho de Assis <Alan Carvalho de Assis>
 *            Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bas_error.h"
#include "bas_value.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define _(String) String

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const char *typestr[] =
{
  (const char *)0,
  (const char *)0,
  "integer",
  (const char *)0,
  "real",
  "string",
  "void"
};

/* for xgettext */

const enum ValueType Value_commonType[V_VOID + 1][V_VOID + 1] =
{
  { 0, 0,       0,         0,       0,       0,        0       },
  { 0, V_ERROR, V_ERROR,   V_ERROR, V_ERROR, V_ERROR,  V_ERROR },
  { 0, V_ERROR, V_INTEGER, V_ERROR, V_REAL,  V_ERROR,  V_ERROR },
  { 0, V_ERROR, V_ERROR,   V_ERROR, V_ERROR, V_ERROR,  V_ERROR },
  { 0, V_ERROR, V_REAL,    V_ERROR, V_REAL,  V_ERROR,  V_ERROR },
  { 0, V_ERROR, V_ERROR,   V_ERROR, V_ERROR, V_STRING, V_ERROR },
  { 0, V_ERROR, V_ERROR,   V_ERROR, V_ERROR, V_ERROR,  V_ERROR }
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static void format_double(struct String *buf, double value, int width,
                          int precision, int exponent)
{
  if (exponent)
    {
      size_t len;
      char *e;
      int en;

      len = buf->length;
      String_appendPrintf(buf, "%.*E", width - 1 - (precision >= 0), value);
      if (buf->character[len + 1] == '.')
        {
          String_delete(buf, len + 1, 1);
        }

      if (precision >= 0)
        {
          String_insertChar(buf, len + width - precision - 1, '.');
        }

      for (e = buf->character + buf->length - 1;
           e >= buf->character && *e != 'E';
           --e);
      ++e;

      en = strtol(e, (char **)0, 10);
      en = en + 2 - (width - precision);
      len = e - buf->character;
      String_delete(buf, len, buf->length - len);
      String_appendPrintf(buf, "%+0*d", exponent - 1, en);
    }
  else if (precision > 0)
    {
      String_appendPrintf(buf, "%.*f", precision, value);
    }
  else if (precision == 0)
    {
      String_appendPrintf(buf, "%.f.", value);
    }
  else if (width)
    {
      String_appendPrintf(buf, "%.f", value);
    }
  else
    {
      double x = value;

      if (x < 0.0001 || x >= 10000000.0)        /* print scientific notation */
        {
          String_appendPrintf(buf, "%.7g", value);
        }
      else                      /* print decimal numbers or integers, if
                                 * possible */
        {
          int o, n, p = 6;

          while (x >= 10.0 && p > 0)
            {
              x /= 10.0;
              --p;
            }

          o = buf->length;
          String_appendPrintf(buf, "%.*f", p, value);
          n = buf->length;
          if (memchr(buf->character + o, '.', n - o))
            {
              while (buf->character[buf->length - 1] == '0')
                {
                  --buf->length;
                }
              if (buf->character[buf->length - 1] == '.')
                {
                  --buf->length;
                }
            }
        }
    }
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

double Value_trunc(double d)
{
  return (d < 0.0 ? ceil(d) : floor(d));
}

double Value_round(double d)
{
  return (d < 0.0 ? ceil(d - 0.5) : floor(d + 0.5));
}

long int Value_toi(double d, int *overflow)
{
  d = Value_round(d);
  *overflow = (d < LONG_MIN || d > LONG_MAX);
  return lrint(d);
}

long int Value_vali(const char *s, char **end, int *overflow)
{
  long int n;

  errno = 0;
  if (*s == '&' && tolower(*(s + 1)) == 'h')
    {
      n = strtoul(s + 2, end, 16);
    }
  else if (*s == '&' && tolower(*(s + 1)) == 'o')
    {
      n = strtoul(s + 2, end, 8);
    }
  else
    {
      n = strtol(s, end, 10);
    }

  *overflow = (errno == ERANGE);
  return n;
}

double Value_vald(const char *s, char **end, int *overflow)
{
  double d;

  errno = 0;
  d = strtod(s, end);
  *overflow = (errno == ERANGE);
  return d;
}

struct Value *Value_new_NIL(struct Value *this)
{
  assert(this != (struct Value *)0);
  this->type = V_NIL;
  return this;
}

struct Value *Value_new_ERROR(struct Value *this, int code, const char *error,
                              ...)
{
  va_list ap;
  char buf[128];

  assert(this != (struct Value *)0);
  va_start(ap, error);
  vsprintf(buf, error, ap);
  va_end(ap);
  this->type = V_ERROR;
  this->u.error.code = code;
  this->u.error.msg = strcpy(malloc(strlen(buf) + 1), buf);
  return this;
}

struct Value *Value_new_INTEGER(struct Value *this, int n)
{
  assert(this != (struct Value *)0);
  this->type = V_INTEGER;
  this->u.integer = n;
  return this;
}

struct Value *Value_new_REAL(struct Value *this, double n)
{
  assert(this != (struct Value *)0);
  this->type = V_REAL;
  this->u.real = n;
  return this;
}

struct Value *Value_new_STRING(struct Value *this)
{
  assert(this != (struct Value *)0);
  this->type = V_STRING;
  String_new(&this->u.string);
  return this;
}

struct Value *Value_new_VOID(struct Value *this)
{
  assert(this != (struct Value *)0);
  this->type = V_VOID;
  return this;
}

struct Value *Value_new_null(struct Value *this, enum ValueType type)
{
  assert(this != (struct Value *)0);
  switch (type)
    {
    case V_INTEGER:
      {
        this->type = V_INTEGER;
        this->u.integer = 0;
        break;
      }

    case V_REAL:
      {
        this->type = V_REAL;
        this->u.real = 0.0;
        break;
      }

    case V_STRING:
      {
        this->type = V_STRING;
        String_new(&this->u.string);
        break;
      }

    case V_VOID:
      {
        this->type = V_VOID;
        break;
      }

    default:
      assert(0);
    }

  return this;
}

int Value_isNull(const struct Value *this)
{
  switch (this->type)
    {
    case V_INTEGER:
      return (this->u.integer == 0);

    case V_REAL:
      return (this->u.real == 0.0);

    case V_STRING:
      return (this->u.string.length == 0);

    default:
      assert(0);
    }

  return -1;
}

void Value_destroy(struct Value *this)
{
  assert(this != (struct Value *)0);
  switch (this->type)
    {
    case V_ERROR:
      free(this->u.error.msg);
      break;

    case V_INTEGER:
      break;

    case V_NIL:
      break;

    case V_REAL:
      break;

    case V_STRING:
      String_destroy(&this->u.string);
      break;

    case V_VOID:
      break;

    default:
      assert(0);
    }

  this->type = 0;
}

struct Value *Value_clone(struct Value *this, const struct Value *original)
{
  assert(this != (struct Value *)0);
  assert(original != (struct Value *)0);
  switch (original->type)
    {
    case V_ERROR:
      {
        strcpy(this->u.error.msg =
               malloc(strlen(original->u.error.msg) + 1),
               original->u.error.msg);
        this->u.error.code = original->u.error.code;
        break;
      }

    case V_INTEGER:
      this->u.integer = original->u.integer;
      break;

    case V_NIL:
      break;

    case V_REAL:
      this->u.real = original->u.real;
      break;

    case V_STRING:
      String_clone(&this->u.string, &original->u.string);
      break;

    default:
      assert(0);
    }

  this->type = original->type;
  return this;
}

struct Value *Value_uplus(struct Value *this, int calc)
{
  switch (this->type)
    {
    case V_INTEGER:
    case V_REAL:
      {
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDUOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_uneg(struct Value *this, int calc)
{
  switch (this->type)
    {
    case V_INTEGER:
      {
        if (calc)
          {
            this->u.integer = -this->u.integer;
          }
        break;
      }

    case V_REAL:
      {
        if (calc)
          {
            this->u.real = -this->u.real;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDUOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_unot(struct Value *this, int calc)
{
  switch (this->type)
    {
    case V_INTEGER:
      {
        if (calc)
          {
            this->u.integer = ~this->u.integer;
          }
        break;
      }

    case V_REAL:
      {
        Value_retype(this, V_INTEGER);
        if (calc)
          {
            this->u.integer = ~this->u.integer;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDUOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_add(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer += x->u.integer;
          }
        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            this->u.real += x->u.real;
          }
        break;
      }

    case V_STRING:
      {
        if (calc)
          {
            String_appendString(&this->u.string, &x->u.string);
          }
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_sub(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer -= x->u.integer;
          }
        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            this->u.real -= x->u.real;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_mult(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer *= x->u.integer;
          }

        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            this->u.real *= x->u.real;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_div(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            if (x->u.real == 0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "Division by zero");
              }
            else
              {
                this->u.real /= x->u.real;
              }
          }
        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            if (x->u.real == 0.0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "Division by zero");
              }
            else
              {
                this->u.real /= x->u.real;
              }
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_idiv(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            if (x->u.integer == 0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "Division by zero");
              }
            else
              {
                this->u.integer /= x->u.integer;
              }
          }
        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            if (x->u.real == 0.0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "Division by zero");
              }
            else
              {
                this->u.real = Value_trunc(this->u.real / x->u.real);
              }
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_mod(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            if (x->u.integer == 0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "Modulo by zero");
              }
            else
              {
                this->u.integer %= x->u.integer;
              }
          }
        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            if (x->u.real == 0.0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "Modulo by zero");
              }
            else
              {
                this->u.real = fmod(this->u.real, x->u.real);
              }
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_pow(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            if (this->u.integer == 0 && x->u.integer == 0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "0^0");
              }
            else if (x->u.integer > 0)
              {
                this->u.integer = pow(this->u.integer, x->u.integer);
              }
            else
              {
                long int thisi = this->u.integer;
                Value_destroy(this);
                Value_new_REAL(this, pow(thisi, x->u.integer));
              }
          }
        break;
      }

    case V_REAL:
      {
        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            if (this->u.real == 0.0 && x->u.real == 0.0)
              {
                Value_destroy(this);
                Value_new_ERROR(this, UNDEFINED, "0^0");
              }
            else
              {
                this->u.real = pow(this->u.real, x->u.real);
              }
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_and(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
    case V_REAL:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer &= x->u.integer;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_or(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
    case V_REAL:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer |= x->u.integer;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_xor(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
    case V_REAL:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer ^= x->u.integer;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_eqv(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
    case V_REAL:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = ~(this->u.integer ^ x->u.integer);
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_imp(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
    case V_REAL:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (~this->u.integer) | x->u.integer;
          }
        break;
      }

    case V_STRING:
      {
        Value_destroy(this);
        Value_new_ERROR(this, INVALIDOPERAND);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_lt(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (this->u.integer < x->u.integer) ? -1 : 0;
          }
        break;
      }

    case V_REAL:
      {
        int v;

        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            v = (this->u.real < x->u.real) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    case V_STRING:
      {
        int v;

        if (calc)
          {
            v = (String_cmp(&this->u.string, &x->u.string) < 0) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_le(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (this->u.integer <= x->u.integer) ? -1 : 0;
          }
        break;
      }

    case V_REAL:
      {
        int v;

        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            v = (this->u.real <= x->u.real) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    case V_STRING:
      {
        int v;

        if (calc)
          {
            v = (String_cmp(&this->u.string, &x->u.string) <= 0) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_eq(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (this->u.integer == x->u.integer) ? -1 : 0;
          }
        break;
      }

    case V_REAL:
      {
        int v;

        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            v = (this->u.real == x->u.real) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }
    case V_STRING:
      {
        int v;

        if (calc)
          {
            v = (String_cmp(&this->u.string, &x->u.string) == 0) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_ge(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (this->u.integer >= x->u.integer) ? -1 : 0;
          }
        break;
      }

    case V_REAL:
      {
        int v;

        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            v = (this->u.real >= x->u.real) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    case V_STRING:
      {
        int v;

        if (calc)
          {
            v = (String_cmp(&this->u.string, &x->u.string) >= 0) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_gt(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (this->u.integer > x->u.integer) ? -1 : 0;
          }
        break;
      }

    case V_REAL:
      {
        int v;

        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            v = (this->u.real > x->u.real) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    case V_STRING:
      {
        int v;

        if (calc)
          {
            v = (String_cmp(&this->u.string, &x->u.string) > 0) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

struct Value *Value_ne(struct Value *this, struct Value *x, int calc)
{
  switch (Value_commonType[this->type][x->type])
    {
    case V_INTEGER:
      {
        VALUE_RETYPE(this, V_INTEGER);
        VALUE_RETYPE(x, V_INTEGER);
        if (calc)
          {
            this->u.integer = (this->u.integer != x->u.integer) ? -1 : 0;
          }
        break;
      }

    case V_REAL:
      {
        int v;

        VALUE_RETYPE(this, V_REAL);
        VALUE_RETYPE(x, V_REAL);
        if (calc)
          {
            v = (this->u.real != x->u.real) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    case V_STRING:
      {
        int v;

        if (calc)
          {
            v = String_cmp(&this->u.string, &x->u.string) ? -1 : 0;
          }
        else
          {
            v = 0;
          }

        Value_destroy(this);
        Value_new_INTEGER(this, v);
        break;
      }

    default:
      assert(0);
    }

  return this;
}

int Value_exitFor(struct Value *this, struct Value *limit, struct Value *step)
{
  switch (this->type)
    {
    case V_INTEGER:
      return
        (step->u.integer < 0
         ? (this->u.integer < limit->u.integer)
         : (this->u.integer > limit->u.integer));

    case V_REAL:
      return
        (step->u.real < 0.0
         ? (this->u.real < limit->u.real) : (this->u.real > limit->u.real));

    case V_STRING:
      return (String_cmp(&this->u.string, &limit->u.string) > 0);

    default:
      assert(0);
    }

  return -1;
}

void Value_errorPrefix(struct Value *this, const char *prefix)
{
  size_t prefixlen, msglen;

  assert(this->type == V_ERROR);
  prefixlen = strlen(prefix);
  msglen = strlen(this->u.error.msg);
  this->u.error.msg = realloc(this->u.error.msg, prefixlen + msglen + 1);
  memmove(this->u.error.msg + prefixlen, this->u.error.msg, msglen);
  memcpy(this->u.error.msg, prefix, prefixlen);
}

void Value_errorSuffix(struct Value *this, const char *suffix)
{
  size_t suffixlen, msglen;

  assert(this->type == V_ERROR);
  suffixlen = strlen(suffix);
  msglen = strlen(this->u.error.msg);
  this->u.error.msg = realloc(this->u.error.msg, suffixlen + msglen + 1);
  memcpy(this->u.error.msg + msglen, suffix, suffixlen + 1);
}

struct Value *Value_new_typeError(struct Value *this, enum ValueType t1,
                                  enum ValueType t2)
{
  assert(typestr[t1]);
  assert(typestr[t2]);
  return Value_new_ERROR(this, TYPEMISMATCH1, _(typestr[t1]), _(typestr[t2]));
}

static void retypeError(struct Value *this, enum ValueType to)
{
  enum ValueType thisType = this->type;

  assert(typestr[thisType]);
  assert(typestr[to]);
  Value_destroy(this);
  Value_new_ERROR(this, TYPEMISMATCH1, _(typestr[thisType]), _(typestr[to]));
}

struct Value *Value_retype(struct Value *this, enum ValueType type)
{
  switch (this->type)
    {
    case V_INTEGER:
      {
        switch (type)
          {
          case V_INTEGER:
            break;

          case V_REAL:
            this->u.real = this->u.integer;
            this->type = type;
            break;

          case V_VOID:
            Value_destroy(this);
            Value_new_VOID(this);
            break;

          default:
            retypeError(this, type);
            break;
          }
        break;
      }

    case V_REAL:
      {
        int overflow;

        switch (type)
          {
          case V_INTEGER:
            {
              this->u.integer = Value_toi(this->u.real, &overflow);
              this->type = V_INTEGER;
              if (overflow)
                {
                  Value_destroy(this);
                  Value_new_ERROR(this, OUTOFRANGE, typestr[V_INTEGER]);
                }
              break;
            }

          case V_REAL:
            break;

          case V_VOID:
            Value_destroy(this);
            Value_new_VOID(this);
            break;

          default:
            retypeError(this, type);
            break;
          }
        break;
      }

    case V_STRING:
      {
        switch (type)
          {
          case V_STRING:
            break;

          case V_VOID:
            Value_destroy(this);
            Value_new_VOID(this);
            break;

          default:
            retypeError(this, type);
            break;
          }
        break;
      }

    case V_VOID:
      {
        switch (type)
          {
          case V_VOID:
            break;

          default:
            retypeError(this, type);
          }
        break;
      }

    case V_ERROR:
      break;

    default:
      assert(0);
    }

  return this;
}

struct String *Value_toString(struct Value *this, struct String *s, char pad,
                              int headingsign, size_t width, int commas,
                              int dollar, int dollarleft, int precision,
                              int exponent, int trailingsign)
{
  size_t oldlength = s->length;

  switch (this->type)
    {
    case V_ERROR:
      String_appendChars(s, this->u.error.msg);
      break;

    case V_REAL:
    case V_INTEGER:
      {
        int sign;
        struct String buf;
        size_t totalwidth = width;

        String_new(&buf);
        if (this->type == V_INTEGER)
          {
            if (this->u.integer < 0)
              {
                sign = -1;
                this->u.integer = -this->u.integer;
              }
            else if (this->u.integer == 0)
              {
                sign = 0;
              }
            else
              {
                sign = 1;
              }
          }
        else
          {
            if (this->u.real < 0.0)
              {
                sign = -1;
                this->u.real = -this->u.real;
              }
            else if (this->u.real == 0.0)
              {
                sign = 0;
              }
            else
              {
                sign = 1;
              }
          }

        switch (headingsign)
          {
          case -1:
            {
              ++totalwidth;
              String_appendChar(&buf, sign == -1 ? '-' : ' ');
              break;
            }

          case 0:
            {
              if (sign == -1)
                {
                  String_appendChar(&buf, '-');
                }
              break;
            }

          case 1:
            {
              ++totalwidth;
              String_appendChar(&buf, sign == -1 ? '-' : '+');
              break;
            }

          case 2:
            break;

          default:
            assert(0);
          }

        totalwidth += exponent;
        if (this->type == V_INTEGER)
          {
            if (precision > 0 || exponent)
              {
                format_double(&buf, (double)this->u.integer, width, precision,
                              exponent);
              }
            else if (precision == 0)
              {
                String_appendPrintf(&buf, "%lu.", this->u.integer);
              }
            else
              {
                String_appendPrintf(&buf, "%lu", this->u.integer);
              }
          }
        else
          {
            format_double(&buf, this->u.real, width, precision, exponent);
          }

        if (commas)
          {
            size_t digits;
            int first;

            first = (headingsign ? 1 : 0);
            for (digits = first;
                 digits < buf.length && buf.character[digits] >= '0' &&
                 buf.character[digits] <= '9'; ++digits);

            while (digits > first + 3)
              {
                digits -= 3;
                String_insertChar(&buf, digits, ',');
              }
          }

        if (dollar)
          {
            String_insertChar(&buf, 0, '$');
          }

        if (trailingsign == -1)
          {
            ++totalwidth;
            String_appendChar(&buf, sign == -1 ? '-' : ' ');
          }
        else if (trailingsign == 1)
          {
            ++totalwidth;
            String_appendChar(&buf, sign == -1 ? '-' : '+');
          }

        String_size(s,
                    oldlength + (totalwidth >
                                 buf.length ? totalwidth : buf.length));

        if (totalwidth > buf.length)
          {
            memset(s->character + oldlength, pad,
                   totalwidth - buf.length + dollarleft);
          }

        memcpy(s->character + oldlength +
               (totalwidth >
                buf.length ? (totalwidth - buf.length) : 0) + dollarleft,
               buf.character + dollarleft, buf.length - dollarleft);

        if (dollarleft)
          {
            s->character[oldlength] = '$';
          }

        String_destroy(&buf);
        break;
      }

    case V_STRING:
      {
        if (width > 0)
          {
            size_t blanks =
              (this->u.string.length <
               width ? (width - this->u.string.length) : 0);

            String_size(s, oldlength + width);
            memcpy(s->character + oldlength, this->u.string.character,
                   blanks ? this->u.string.length : width);
            if (blanks)
              {
                memset(s->character + oldlength + this->u.string.length, ' ',
                       blanks);
              }
          }
        else
          {
            String_appendString(s, &this->u.string);
          }
        break;
      }

    default:
      assert(0);
      return 0;
    }

  return s;
}

struct Value *Value_toStringUsing(struct Value *this, struct String *s,
                                  struct String *using, size_t * usingpos)
{
  char pad = ' ';
  int headingsign;
  int width = 0;
  int commas = 0;
  int dollar = 0;
  int dollarleft = 0;
  int precision = -1;
  int exponent = 0;
  int trailingsign = 0;

  headingsign = (using->length ? 0 : -1);
  if (*usingpos == using->length)
    {
      *usingpos = 0;
    }

  while (*usingpos < using->length)
    {
      switch (using->character[*usingpos])
        {
        case '_':              /* output next char */
          {
            ++(*usingpos);
            if (*usingpos < using->length)
              {
                String_appendChar(s, using->character[(*usingpos)++]);
              }
            else
              {
                Value_destroy(this);
                return Value_new_ERROR(this, MISSINGCHARACTER);
              }

            break;
          }

        case '!':              /* output first character of string */
          {
            width = 1;
            ++(*usingpos);
            goto work;
          }

        case '\\':             /* output n characters of string */
          {
            width = 1;
            ++(*usingpos);
            while (*usingpos < using->length &&
                   using->character[*usingpos] == ' ')
              {
                ++(*usingpos);
                ++width;
              }

            if (*usingpos < using->length &&
                using->character[*usingpos] == '\\')
              {
                ++(*usingpos);
                ++width;
                goto work;
              }
            else
              {
                Value_destroy(this);
                return Value_new_ERROR(this, IOERROR,
                                       _("unpaired \\ in format"));
              }

            break;
          }
        case '&':              /* output string */
          {
            width = 0;
            ++(*usingpos);
            goto work;
          }
        case '*':
        case '$':
        case '0':
        case '+':
        case '#':
        case '.':
          {
            if (using->character[*usingpos] == '+')
              {
                headingsign = 1;
                ++(*usingpos);
              }

            while (*usingpos < using->length &&
                   strchr("$#*0,", using->character[*usingpos]))
              {
                switch (using->character[*usingpos])
                  {
                  case '$':
                    if (width == 0)
                      {
                        dollarleft = 1;
                      }

                    if (++dollar > 1)
                      {
                        ++width;
                      }
                    break;

                  case '*':
                    pad = '*';
                    ++width;
                    break;

                  case '0':
                    pad = '0';
                    ++width;
                    break;

                  case ',':
                    commas = 1;
                    ++width;
                    break;

                  default:
                    ++width;
                  }
                ++(*usingpos);
              }

            if (*usingpos < using->length && using->character[*usingpos] == '.')
              {
                ++(*usingpos);
                ++width;
                precision = 0;
                while (*usingpos < using->length &&
                       strchr("*#", using->character[*usingpos]))
                  {
                    ++(*usingpos);
                    ++precision;
                    ++width;
                  }

                if (width == 1 && precision == 0)
                  {
                    Value_destroy(this);
                    return Value_new_ERROR(this, BADFORMAT);
                  }
              }

            if (*usingpos < using->length && using->character[*usingpos] == '-')
              {
                ++(*usingpos);
                if (headingsign == 0)
                  {
                    headingsign = 2;
                  }
                trailingsign = -1;
              }
            else if (*usingpos < using->length &&
                     using->character[*usingpos] == '+')
              {
                ++(*usingpos);
                if (headingsign == 0)
                  {
                    headingsign = 2;
                  }
                trailingsign = 1;
              }

            while (*usingpos < using->length &&
                   using->character[*usingpos] == '^')
              {
                ++(*usingpos);
                ++exponent;
              }

            goto work;
          }

        default:
          {
            String_appendChar(s, using->character[(*usingpos)++]);
          }
        }
    }

work:
  Value_toString(this, s, pad, headingsign, width, commas, dollar, dollarleft,
                 precision, exponent, trailingsign);
  if ((this->type == V_INTEGER || this->type == V_REAL) && width == 0 &&
      precision == -1)
    {
      String_appendChar(s, ' ');
    }

  while (*usingpos < using->length)
    {
      switch (using->character[*usingpos])
        {
        case '_':              /* output next char */
          {
            ++(*usingpos);
            if (*usingpos < using->length)
              {
                String_appendChar(s, using->character[(*usingpos)++]);
              }
            else
              {
                Value_destroy(this);
                return Value_new_ERROR(this, MISSINGCHARACTER);
              }
            break;
          }

        case '!':
        case '\\':
        case '&':
        case '*':
        case '0':
        case '+':
        case '#':
        case '.':
          return this;

        default:
          {
            String_appendChar(s, using->character[(*usingpos)++]);
          }
        }
    }

  return this;
}

struct String *Value_toWrite(struct Value *this, struct String *s)
{
  switch (this->type)
    {
    case V_INTEGER:
      String_appendPrintf(s, "%ld", this->u.integer);
      break;

    case V_REAL:
      {
        double x;
        int p = DBL_DIG;
        int n, o;

        x = (this->u.real < 0.0 ? -this->u.real : this->u.real);
        while (x > 1.0 && p > 0)
          {
            x /= 10.0;
            --p;
          }

        o = s->length;
        String_appendPrintf(s, "%.*f", p, this->u.real);
        n = s->length;
        if (memchr(s->character + o, '.', n - o))
          {
            while (s->character[s->length - 1] == '0')
              {
                --s->length;
              }

            if (s->character[s->length - 1] == '.')
              {
                --s->length;
              }
          }
        break;
      }

    case V_STRING:
      {
        size_t l = this->u.string.length;
        char *data = this->u.string.character;

        String_appendChar(s, '"');
        while (l--)
          {
            if (*data == '"')
              {
                String_appendChar(s, '"');
              }

            String_appendChar(s, *data);
            ++data;
          }

        String_appendChar(s, '"');
        break;
      }

    default:
      assert(0);
    }

  return s;
}

struct Value *Value_nullValue(enum ValueType type)
{
  static struct Value integer = { V_INTEGER };
  static struct Value real = { V_REAL };
  static struct Value string = { V_STRING };
  static char n[] = "";
  static int init = 0;

  if (!init)
    {
      integer.u.integer = 0;
      real.u.real = 0.0;
      string.u.string.length = 0;
      string.u.string.character = n;
    }

  switch (type)
    {
    case V_INTEGER:
      return &integer;

    case V_REAL:
      return &real;

    case V_STRING:
      return &string;

    default:
      assert(0);
    }

  return (struct Value *)0;
}

long int lrint(double d)
{
  return d;
}
