//===--- NumericParsing.swift.gyb -----------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// -*- swift -*-
// RUN: %empty-directory(%t)
// RUN: %gyb -DCMAKE_SIZEOF_VOID_P=%target-ptrsize %s -o %t/NumericParsing.swift
// RUN: %line-directive %t/NumericParsing.swift -- %target-build-swift %t/NumericParsing.swift -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %line-directive %t/NumericParsing.swift -- %target-run %t/a.out
// REQUIRES: executable_test
%{
from SwiftIntTypes import all_integer_types

word_bits = int(CMAKE_SIZEOF_VOID_P)

def maskOfWidth(n):
  return (1 << n) - 1

def inRadix(radix, n, zero='0'):
  """
Represent the int n in the given radix.
Note: the third parameter, zero, is not for user consumption.
  """
  if n < 0:
    return '-' + inRadix(radix, -n, '')
  elif n == 0:
    return zero
  else:
    r = n % radix
    digit = chr((ord('0') + r) if r < 10 else (ord('a') + r - 10))
    return inRadix(radix, int(n // radix), '') + digit

# The maximal legal radix
max_radix = ord('z') - ord('a') + 1 + 10

# Test a few important radices
radices_to_test = [2, 8, 10, 16, max_radix]

# How many values to test in each radix?  A nice prime number of course.
number_of_values = 23
}%

import StdlibUnittest


var tests = TestSuite("NumericParsing")

% for type in all_integer_types(word_bits):
%   Self = type.stdlib_name
%   maxValue = maskOfWidth((type.bits - 1) if type.is_signed else type.bits)
%   minValue = -maxValue - 1 if type.is_signed else 0
%   required_values = [minValue, 0, maxValue]
tests.test("${Self}/success") {
  let isSigned = ${str(type.is_signed).lower()}

  //===--- Important cases ------------------------------------------------===//
  expectEqual(0, ${Self}("0"))      // zero
  expectEqual(17, ${Self}("17"))    // non-zero with no radix

  // Leading zeroes, with and without a radix
  expectEqual(10, ${Self}("010"))
  expectEqual(15, ${Self}("00F", radix: 16))

  // Leading '+'
  expectEqual(0, ${Self}("+0"))
  expectEqual(10, ${Self}("+10"))
  expectEqual(10, ${Self}("+010"))
  expectEqual(15, ${Self}("+00F", radix: 16))

  // Negative numbers
  expectEqual(${-128 if type.is_signed else 'nil'}, ${Self}("-0080", radix: 16))
  expectEqual(0, ${Self}("-0"))
  expectEqual(0, ${Self}("-00", radix: 16))

  // Maximum radix
  expectEqual(${max_radix} - 1, ${Self}("z", radix: ${max_radix})!)

  // Out-of-range values
  expectEqual(nil, ${Self}("${maxValue + 1}"))
  expectEqual(nil, ${Self}("${minValue - 1}"))
  expectEqual(nil, ${Self}("\u{1D7FF}"))  // MATHEMATICAL MONOSPACE DIGIT NINE

  // Cases that should fail to parse
  expectEqual(nil, ${Self}("--0")) // Zero w/ repeated plus
  expectEqual(nil, ${Self}("-+5")) // Non-zero with -+

  // Do more exhaustive testing
  % for radix in radices_to_test:
  %   for n in required_values + list(range(
  %     minValue + 1, maxValue - 1,
  %     int((maxValue - minValue - 2) // (number_of_values - len(required_values))))):
  %     prefix = '+' if n > 0 and n % 2 == 0 else '' # leading '+'
  %     text = inRadix(radix, n)
  expectEqual(${n}, ${Self}("${prefix + text}", radix: ${radix}))
  %     if text != text.upper():
  expectEqual(${n}, ${Self}("${prefix + text.upper()}", radix: ${radix}))
  %     end
  %   end
  % end
}

tests.test("${Self}/radixTooLow")
.require(.crashTesting)
.code {
  ${Self}("0", radix: 2)
  expectCrashLater()
  ${Self}("0", radix: 1)
}

tests.test("${Self}/radixTooHigh")
.require(.crashTesting)
.code {
  let maxRadix = ${ord('z') - ord('a') + 1 + 10}
  ${Self}("0", radix: maxRadix)
  expectCrashLater()
  let y = ${Self}("0", radix: maxRadix + 1)
}

% end

tests.test("FixedWidthInteger/maxUInt64") {
  func f<T : FixedWidthInteger>(_ x: T) -> String {
    return String(x, radix: 16)
  }
  let x = f(UInt64.max)
  let y = String(UInt64.max, radix: 16)
  expectEqual(x, y)
}

% for Self in 'Float', 'Double', 'Float80':

% if Self == 'Float80':
#if !os(Windows) && (arch(i386) || arch(x86_64))
% end

tests.test("${Self}/Basics") {
% if Self != 'Float80': # Inf/NaN are not defined for Float80
  expectEqual(.infinity, ${Self}("inf"))
  expectEqual(.infinity, ${Self}("Inf"))
  expectEqual(-(.infinity), ${Self}("-inf"))
  expectEqual(-(.infinity), ${Self}("-Inf"))
  expectEqual(String(${Self}.nan), String(${Self}("nan")!))
  expectEqual(String(${Self}.nan), String(${Self}("NaN")!))

// sNaN cannot be fully supported on i386.
#if !arch(i386)
  expectTrue(${Self}("sNaN")!.isSignalingNaN)
  expectTrue(${Self}("SNAN")!.isSignalingNaN)
  expectTrue(${Self}("+snan")!.isSignalingNaN)
  expectTrue(${Self}("-SnAn")!.isSignalingNaN)
#endif
% end

  expectEqual(-0.0, ${Self}("-0"))
  expectEqual(-0.0, ${Self}("-0.0"))
  expectEqual(0.0, ${Self}("0"))
  expectEqual(0.0, ${Self}("0.0"))
  expectEqual(64206, ${Self}("0xFACE")) // Yes, strtoXXX supports hex.

  // Check that we can round-trip the string representation of 2^100,
  // which requires an exponent to express...
  let large =
    repeatElement(1024 as ${Self}, count: 10).reduce(1, *)
  let largeText = String(large)
  expectEqual(largeText, String(${Self}(largeText)!))

  // ...ditto for its inverse.
  let smallText = String(1 / large)
  expectEqual(smallText, String(${Self}(smallText)!))

  // Cases that should fail to parse
  expectNil(${Self}(""))       // EMPTY
  expectNil(${Self}("0FACE"))  // Hex characters without 0x
  expectNil(${Self}("99x"))
  expectNil(${Self}(" 0"))  // Leading whitespace
  expectNil(${Self}("0 "))  // Trailing whitespace
  expectNil(${Self}("\u{1D7FF}"))  // MATHEMATICAL MONOSPACE DIGIT NINE

  // Values that are too large/small:
  // * Early versions of Swift returned `nil` for e.g., 1e99999 or 1e-99999
  // * After 5.3, large/small values return .infinity or zero, respectively
  // These are tested in NumericParsing2.swift.gyb
}

% if Self == 'Float80':
#endif
% end

% end
runAllTests()
