//===--- Integers.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
//
//===----------------------------------------------------------------------===//
// RUN: %empty-directory(%t)
// RUN: %gyb -DWORD_BITS=%target-ptrsize %s -o %t/Integers.swift
// RUN: %line-directive %t/Integers.swift -- %target-build-swift %t/Integers.swift -swift-version 4 -Onone -o %t/a.out
// RUN: %line-directive %t/Integers.swift -- %target-codesign %t/a.out
// RUN: %line-directive %t/Integers.swift -- %target-run %t/a.out
// REQUIRES: executable_test

// FIXME: this test runs forever on iOS arm64
// REQUIRES: OS=macosx

%{
word_bits = int(WORD_BITS) // 2
from SwiftIntTypes import all_integer_types
}%

/// Prints the message if the body is uncommented; used for
/// diagnostics.
@_transparent
public func _log(_ message: @autoclosure () -> String) {
  // print(message())
}

extension FixedWidthInteger {
  /// a hex representation of every bit in the number
  func hexBits(_ bitWidth: Int) -> String {
    let hexDigits: [Unicode.Scalar] = [
      "0", "1", "2", "3", "4", "5", "6", "7",
      "8", "9", "A", "B", "C", "D", "E", "F"]

    var result = "".unicodeScalars
    var x = self
    var nibbles: Int = 0
    repeat {
      if nibbles % 4 == 0 && nibbles != 0 {
        result.insert("_", at: result.startIndex)
      }
      let lowUWord = x.words.first ?? 0
      result.insert(
        hexDigits[Int(lowUWord._value) & 0xF],
        at: result.startIndex
      )
      x /= 16
      nibbles += 1
    }
    while (nibbles << 2 < bitWidth || (x != 0 && x + 1 != 0))
    return (self < 0 ? "[-]" : "[+]") + String(result)
  }

  var hex: String { return hexBits(0) }
}

typealias Word = Int${word_bits}
typealias UWord = UInt${word_bits}
typealias DWord = Int${word_bits*2}
typealias UDWord = UInt${word_bits*2}

struct MockBinaryInteger<T : BinaryInteger> {
  var _value: T

  init(_ value: T) {
    _value = value
  }
}

extension MockBinaryInteger : CustomStringConvertible {
  var description: String {
    return _value.description
  }
}

extension MockBinaryInteger : ExpressibleByIntegerLiteral {
  init(integerLiteral value: T.IntegerLiteralType) {
    _value = T(integerLiteral: value)
  }
}

extension MockBinaryInteger : Comparable {
  static func < (lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) -> Bool {
    return lhs._value < rhs._value
  }

  static func == (
    lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
  ) -> Bool {
    return lhs._value == rhs._value
  }
}

extension MockBinaryInteger : Hashable {
  func hash(into hasher: inout Hasher) {
    hasher.combine(_value)
  }
}

extension MockBinaryInteger : BinaryInteger {
  static var isSigned: Bool {
    return T.isSigned
  }

  init<Source>(_ source: Source) where Source : BinaryFloatingPoint {
    _value = T(source)
  }

  init?<Source>(exactly source: Source) where Source : BinaryFloatingPoint {
    guard let result = T(exactly: source) else { return nil }
    _value = result
  }

  init<Source>(_ source: Source) where Source : BinaryInteger {
    _value = T(source)
  }

  init?<Source>(exactly source: Source) where Source : BinaryInteger {
    guard let result = T(exactly: source) else { return nil }
    _value = result
  }

  init<Source>(truncatingIfNeeded source: Source) where Source : BinaryInteger {
    _value = T(truncatingIfNeeded: source)
  }

  init<Source>(clamping source: Source) where Source : BinaryInteger {
    _value = T(clamping: source)
  }

  var magnitude: MockBinaryInteger<T.Magnitude> {
    return MockBinaryInteger<T.Magnitude>(_value.magnitude)
  }

  var words: T.Words {
    return _value.words
  }

  var bitWidth: Int {
    return _value.bitWidth
  }

  var trailingZeroBitCount: Int {
    return _value.trailingZeroBitCount
  }

  static func + (
    lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
  ) -> MockBinaryInteger<T> {
    return MockBinaryInteger(lhs._value + rhs._value)
  }

  static func += (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value += rhs._value
  }

  static func - (
    lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
  ) -> MockBinaryInteger<T> {
    return MockBinaryInteger(lhs._value - rhs._value)
  }

  static func -= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value -= rhs._value
  }

  static func * (
    lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
  ) -> MockBinaryInteger<T> {
    return MockBinaryInteger(lhs._value * rhs._value)
  }

  static func *= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value *= rhs._value
  }

  static func / (
    lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
  ) -> MockBinaryInteger<T> {
    return MockBinaryInteger(lhs._value / rhs._value)
  }

  static func /= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value /= rhs._value
  }

  static func % (
    lhs: MockBinaryInteger<T>, rhs: MockBinaryInteger<T>
  ) -> MockBinaryInteger<T> {
    return MockBinaryInteger(lhs._value % rhs._value)
  }

  static func %= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value %= rhs._value
  }

  static func &= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value &= rhs._value
  }

  static func |= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value |= rhs._value
  }

  static func ^= (lhs: inout MockBinaryInteger<T>, rhs: MockBinaryInteger<T>) {
    lhs._value ^= rhs._value
  }

  static prefix func ~ (x: MockBinaryInteger<T>) -> MockBinaryInteger<T> {
    return MockBinaryInteger(~x._value)
  }

  static func >>= <RHS>(
    lhs: inout MockBinaryInteger<T>, rhs: RHS
  ) where RHS : BinaryInteger {
    lhs._value >>= rhs
  }

  static func <<= <RHS>(
    lhs: inout MockBinaryInteger<T>, rhs: RHS
  ) where RHS : BinaryInteger {
    lhs._value <<= rhs
  }
}

import StdlibUnittest


func expectEqual<T : FixedWidthInteger>(
  _ expected: T, _ actual: T,
  _ message: @autoclosure () -> String = "",
  stackTrace: SourceLocStack = SourceLocStack(),
  showFrame: Bool = true,
  file: String = #file, line: UInt = #line
) {
  if expected != actual {
    expectationFailure(
      "expected: \(String(reflecting: expected))"
      + " (of type \(String(reflecting: type(of: expected))))\n"
      + "  = \(expected.hex)\n"
      + "actual: \(String(reflecting: actual))"
      + " (of type \(String(reflecting: type(of: actual))))\n"
      + "  = \(actual.hex)\n",
      trace: message(),
      stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
  }
}

func expectEqual<T : FixedWidthInteger>(
  _ expected: (T, Bool), _ actual: (T, Bool),
  _ message: @autoclosure () -> String = "",
  stackTrace: SourceLocStack = SourceLocStack(),
  showFrame: Bool = true,
  file: String = #file, line: UInt = #line
) {
% for i in 0, 1:
  expectEqual(
    expected.${i}, actual.${i}, message(),
    stackTrace: stackTrace.pushIf(showFrame, file: file, line: line),
    showFrame: false)
% end
}

var tests = TestSuite("Integers")

tests.test("Literals") {
  // Testing against the official Int types so as not to depend on
  // unimplemented stuff.
  let a: UInt8 = 0b1_0_11_0_111
  expectEqual(a, 0b1_0_11_0_111)

  let b: Int16 = 183
  expectEqual(b, 0b1_0_11_0_111)

  let c: Int16 = -183
  expectEqual(c, -183)

  let d: Int8 = 127
  expectEqual(d, 127)

  let e: UInt8 = 255
  expectEqual(e, 255)
}

tests.test("Equality") {
  expectEqual(183 as UInt8, 183)
  expectNotEqual(183 as UInt8, 184)

  expectEqual(49 as Int8, 49)
  expectNotEqual(-49 as Int8, 49)
}

func indexOrder<T: Comparable>(x: T, y: T)
  -> ExpectedComparisonResult {
  return x < y ? .lt : x > y ? .gt : .eq
}

tests.test("Ordering") {
  checkComparable([127, 183, 184, 255] as [UInt8], oracle: indexOrder)
  checkComparable([-128, -1, 83, 84, 127] as [Int8], oracle: indexOrder)
  checkComparable([127, 183, 184, 255, 65535] as [UInt16], oracle: indexOrder)
  checkComparable([-32768, -32767, 83, 84, 32767] as [Int16], oracle: indexOrder)
}

tests.test("Simple-Arithmetic") {
  expectEqual(1 as Int8 + 2, 3)
  expectEqual(1 as Int8 - 2, -1)
  expectEqual(-5 as Int8 + 11, 6)
  expectEqual(3 as Int8 * 4, 12)
  expectEqual(4 as Int8 * -7, -28)
  expectEqual(-4 as Int8 * -7, 28)
  expectEqual(5 as Int8 / 2, 2)
  expectEqual(6 as Int8 / 2, 3)
  expectEqual(7 as Int8 / 2, 3)
  expectEqual(5 as Int8 % 3, 2)
  expectEqual(6 as Int8 % 3, 0)
  expectEqual(7 as Int8 % 3, 1)
}

% for w in map(lambda x: x.bits, all_integer_types(word_bits)):
%   for prefix in ['U', '']:
%     Type = '{}Int{}'.format(prefix, w)
tests.test("${Type}/Add/Overflow") {
  func f(_ x: ${Type}) -> ${Type} {
    return x + 1
  }
  expectCrashLater()
  _ = f(${Type}.max)
}

tests.test("${Type}/Add/MaskingOverflow") {
  expectEqual(${Type}.min, ${Type}.max &+ 1)
}

tests.test("${Type}/Subtract/Underflow") {
  func f(_ x: ${Type}) -> ${Type} {
    return x - 1
  }
  expectCrashLater()
  _ = f(${Type}.min)
}

tests.test("${Type}/Subtract/MaskingUnderflow") {
  expectEqual(${Type}.max, ${Type}.min &- 1)
}

tests.test("${Type}/Multiply/MaskingOverflow") {
  expectEqual(${Type}.min, (${Type}.max / 2 + 1) &* 2)
}

tests.test("${Type}/AddInPlace/Overflow") {
  func f(_ x: inout ${Type}) {
    x += 1
  }
  expectCrashLater()
  var x = ${Type}.max
  f(&x)
}

tests.test("${Type}/AddInPlace/MaskingOverflow") {
  var x = ${Type}.max
  x &+= 1
  expectEqual(${Type}.min, x)
}

tests.test("${Type}/SubtractInPlace/Underflow") {
  func f(_ x: inout ${Type}) {
    x -= 1
  }
  expectCrashLater()
  var x = ${Type}.min
  f(&x)
}

tests.test("${Type}/SubtractInPlace/MaskingUnderflow") {
  var x = ${Type}.min
  x &-= 1
  expectEqual(${Type}.max, x)
}

tests.test("${Type}/MultiplyInPlace/MaskingOverflow") {
  var x = ${Type}.max / 2 + 1
  x &*= 2
  expectEqual(${Type}.min, x)
}
%   end
% end

tests.test("Simple-Bitwise") {
  expectEqual(0b100_1001 as Int8 >> 1, 0b10_0100)
  expectEqual(-0b100_1001 as Int8 >> 1, -0b10_0101)
  expectEqual(0b1011_0111 as UInt8 >> 1, 0b0101_1011)

  expectEqual(0b100_1001 as Int8 >> 1, 0b10_0100)
  expectEqual(-0b100_1001 as Int8 >> 1, -0b10_0101)
  expectEqual(0b1011_0111 as UInt8 >> 1, 0b0101_1011)

  expectEqual((0b1011_0111 as UInt8) & 0b0110_1110, 0b0010_0110)
  expectEqual((0b1011_0111 as UInt8) | 0b0110_1110, 0xFF)
  expectEqual((0b1011_0111 as UInt8) ^ 0b0110_1110, 0b1101_1001)
}

tests.test("MinMax") {
  expectEqual(255, UInt8.max)
  expectEqual(0, UInt8.min)
  expectEqual(127, Int8.max)
  expectEqual(-128, Int8.min)
}

tests.test("CountLeadingZeros") {
  expectEqual(0, UInt8.max.leadingZeroBitCount)
  expectEqual(8, UInt8.min.leadingZeroBitCount)
  expectEqual(1, Int8.max.leadingZeroBitCount)
  expectEqual(0, Int8.min.leadingZeroBitCount)
}

tests.test("CountTrailingZeros") {
  expectEqual(0, UInt8.max.trailingZeroBitCount)
  expectEqual(8, UInt8.min.trailingZeroBitCount)
  expectEqual(0, Int8.max.trailingZeroBitCount)
  expectEqual(7, Int8.min.trailingZeroBitCount)
}

tests.test("Conversion8to16") {
  expectEqual(255, UInt16(UInt8.max))
  expectEqual(255, Int16(UInt8.max))
  expectEqual(0, UInt16(UInt8.min))
  expectEqual(0, Int16(UInt8.min))
  expectEqual(127, Int16(Int8.max))
  let negativeValue = Int8.min
  expectCrashLater()
  _ = UInt16(negativeValue)
}


tests.test("Conversion16to8") {
  expectEqual(255, UInt8(255 as UInt16))
  expectEqual(255, UInt8(255 as Int16))

  expectEqual(0, UInt8(0 as UInt16))
  expectEqual(0, UInt8(0 as Int16))

  expectEqual(127, Int8(127 as UInt16))
  expectEqual(127, Int8(127 as Int16))

  expectEqual(-128, Int8(-128 as Int16))
  let tooLarge: UInt16 = 128
  expectCrashLater()
  _ = Int8(tooLarge)
}

tests.test("Conversion16to8a") {
  let tooLarge: Int16 = 128
  expectCrashLater()
  _ = Int8(tooLarge)
}

tests.test("Conversion16to8b") {
  let tooLarge: Int16 = 256
  expectCrashLater()
  _ = UInt8(tooLarge)
}

tests.test("Conversion16to8c") {
  let tooLarge: UInt16 = 256
  expectCrashLater()
  _ = UInt8(tooLarge)
}

tests.test("ConversionWordToDWord") {
  expectEqual(1 << ${word_bits} - 1, UDWord(UWord.max))
  expectEqual(1 << ${word_bits} - 1, DWord(UWord.max))
  expectEqual(0, UDWord(UWord.min))
  expectEqual(0, DWord(UWord.min))
  expectEqual(1 << ${word_bits-1} - 1, DWord(Word.max))
  let negativeValue = Word.min
  expectCrashLater()
  _ = UDWord(negativeValue)
}

tests.test("ConversionDWordToWord") {
  expectEqual(~0, UWord(1 << ${word_bits} - 1 as UDWord))
  expectEqual(~0, UWord(1 << ${word_bits} - 1 as DWord))

  expectEqual(0, UWord(0 as UDWord))
  expectEqual(0, UWord(0 as DWord))

  expectEqual(Word.max, Word(1 << ${word_bits-1} - 1 as UDWord))
  expectEqual(Word.max, Word(1 << ${word_bits-1} - 1 as DWord))

  expectEqual(Word.min, Word(-1 << ${word_bits-1} as DWord))
  let tooLarge: UDWord = 1 << ${word_bits-1}
  expectCrashLater()
  _ = Word(tooLarge)
}

tests.test("ConversionDWordToWordA") {
  let tooLarge: DWord = 1 << ${word_bits}
  expectCrashLater()
  _ = Word(tooLarge)
}

tests.test("ConversionDWordToWordB") {
  let tooLarge: DWord = 1 << ${word_bits}
  expectCrashLater()
  _ = UWord(tooLarge)
}

tests.test("ConversionDWordToWordC") {
  let tooLarge: UDWord = 1 << ${word_bits}
  expectCrashLater()
  _ = UWord(tooLarge)
}

tests.test("truncatingIfNeeded") {

  expectEqual(-2, Int8(truncatingIfNeeded: UInt8.max - 1))
  expectEqual(3, Int8(truncatingIfNeeded: 3 as UInt8))
  expectEqual(UInt8.max - 1, UInt8(truncatingIfNeeded: -2 as Int8))
  expectEqual(3, UInt8(truncatingIfNeeded: 3 as Int8))

  expectEqual(-2, DWord(truncatingIfNeeded: UDWord.max - 1))
  expectEqual(3, DWord(truncatingIfNeeded: 3 as UDWord))
  expectEqual(UDWord.max - 1, UDWord(truncatingIfNeeded: -2 as DWord))
  expectEqual(3, UDWord(truncatingIfNeeded: 3 as DWord))

  expectEqual(-2, Int32(truncatingIfNeeded: -2 as Int8))
  expectEqual(3, Int32(truncatingIfNeeded: 3 as Int8))
  expectEqual(127, Int32(truncatingIfNeeded: 127 as UInt8))
  expectEqual(129, Int32(truncatingIfNeeded: 129 as UInt8))
  expectEqual((1 << 31 - 1) << 1, UInt32(truncatingIfNeeded: -2 as Int8))
  expectEqual(3, UInt32(truncatingIfNeeded: 3 as Int8))
  expectEqual(128, UInt32(truncatingIfNeeded: 128 as UInt8))
  expectEqual(129, UInt32(truncatingIfNeeded: 129 as UInt8))

  expectEqual(-2, DWord(truncatingIfNeeded: -2 as Int8))
  expectEqual(3, DWord(truncatingIfNeeded: 3 as Int8))
  expectEqual(127, DWord(truncatingIfNeeded: 127 as UInt8))
  expectEqual(129, DWord(truncatingIfNeeded: 129 as UInt8))
  expectEqual(
    (1 << ${word_bits*2-1} - 1) << 1,
    UDWord(truncatingIfNeeded: -2 as Int8))
  expectEqual(3, UDWord(truncatingIfNeeded: 3 as Int8))
  expectEqual(128, UDWord(truncatingIfNeeded: 128 as UInt8))
  expectEqual(129, UDWord(truncatingIfNeeded: 129 as UInt8))

  expectEqual(-2, Int8(truncatingIfNeeded: -2 as DWord))
  expectEqual(-2, Int8(truncatingIfNeeded: -1 << 67 - 2 as DWord))
  expectEqual(127, Int8(truncatingIfNeeded: 127 as UDWord))
  expectEqual(-127, Int8(truncatingIfNeeded: 129 as UDWord))
  expectEqual(0b1111_1100, UInt8(truncatingIfNeeded: -4 as DWord))
  expectEqual(0b1111_1100, UInt8(truncatingIfNeeded: -1 << 67 - 4 as DWord))
  expectEqual(128, UInt8(truncatingIfNeeded: 128 + 1024 as UDWord))
  expectEqual(129, UInt8(truncatingIfNeeded: 129 + 1024 as UDWord))
}

tests.test("Parsing/LosslessStringConvertible") {
  func _toArray<T: LosslessStringConvertible>(_ text: String) -> [T] {
    return text.split(separator: " ").map { T(String($0)) }.compactMap { $0 }
  }

  expectEqualSequence([1, 2, 3], _toArray("1 2 3") as [Int])
  expectEqualSequence(
    [Int](), _toArray("21-50 ff6600 10000000000000000000000000") as [Int])
}

tests.test("HeterogeneousEquality") {
  expectTrue(-1 as DWord != UDWord.max)
  expectTrue(DWord.max == UDWord.max / 2)
  expectTrue((0 as DWord) == 0 as UDWord)

  expectTrue(-1 as Int8 == -1 as DWord)
  expectTrue(UInt8.max != -1 as DWord)
  expectTrue(UInt8.max == 255 as DWord)
  expectTrue((0 as UInt8) == 0 as DWord)

  expectTrue(UInt8.max == 255 as UDWord)
  expectTrue(UInt8.max != UDWord.max)
  expectTrue((0 as UInt8) == (0 as UDWord))
}

tests.test("HeterogeneousOrdering") {
  expectTrue((-1 as DWord) < UDWord.max)
  expectTrue(DWord.max <= UDWord.max / 2)
  expectTrue(DWord.max >= UDWord.max / 2)
  expectTrue((0 as DWord) <= (0 as UDWord))
  expectTrue((0 as DWord) >= (0 as UDWord))

  expectTrue((-1 as Int8) <= -1 as DWord)
  expectTrue((-1 as Int8) >= -1 as DWord)
  expectTrue(UInt8.max > -1 as DWord)
  expectTrue(UInt8.max <= 255 as DWord)
  expectTrue(UInt8.max >= 255 as DWord)
  expectTrue((0 as UInt8) <= (0 as DWord))
  expectTrue((0 as UInt8) >= (0 as DWord))

  expectTrue(UInt8.max <= 255 as UDWord)
  expectTrue(UInt8.max >= 255 as UDWord)
  expectTrue(UInt8.max < UDWord.max)
  expectTrue((0 as UInt8) <= (0 as UDWord))
  expectTrue((0 as UInt8) >= (0 as UDWord))
}

tests.test("SmartBitShift/Homogeneous/Left/Int16") {
  let all1s = ~0 as Int16
  expectEqual(all1s, all1s << (0 as Int16))
  expectEqual(-2, all1s << (1 as Int16))
  expectEqual(Int16.min, all1s << (15 as Int16))
  expectEqual(0, all1s << (16 as Int16))

  expectEqual(-1, all1s << (-1 as Int16))
  expectEqual(-1, all1s << (-15 as Int16))
  expectEqual(-1, all1s << (-16 as Int16))
}

tests.test("SmartBitShift/Unconstrained/Left/Int16") {
  let all1s = ~0 as Int16
  expectEqual(all1s, all1s << 0)
  expectEqual(-2, all1s << 1)
  expectEqual(Int16.min, all1s << 15)
  expectEqual(0, all1s << 16)

  expectEqual(-1, all1s << -1)
  expectEqual(-1, all1s << -15)
  expectEqual(-1, all1s << -16)
}

tests.test("SmartBitShift/Homogeneous/Left/UInt16") {
  let all1s = ~0 as UInt16
  expectEqual(all1s, all1s << 0)
  expectEqual(0b1111_1111_1111_1110, all1s << 1)
  expectEqual(UInt16.max / 2 + 1, all1s << 15)
  expectEqual(0, all1s << 16)
}

tests.test("SmartBitShift/Heterogeneous/Left/Int16") {
  let all1s = ~0 as Int16
  expectEqual(all1s, all1s << (0 as Int8))
  expectEqual(-2, all1s << (1 as Int32))
  expectEqual(Int16.min, all1s << (15 as UInt32))
  expectEqual(0, all1s << (16 as UInt8))

  expectEqual(-1, all1s << (-1 as DWord))
  expectEqual(-1, all1s << (-15 as Int))
  expectEqual(-1, all1s << (-16 as Int32))
}

tests.test("SmartBitShift/Heterogeneous/Left/UInt16") {
  let all1s = ~0 as UInt16
  expectEqual(all1s, all1s << (0 as Int8))
  expectEqual(0b1111_1111_1111_1110, all1s << (1 as Int32))
  expectEqual(UInt16.max / 2 + 1, all1s << (15 as UInt32))
  expectEqual(0, all1s << (16 as UInt8))

  expectEqual(UInt16.max / 2, all1s << (-1 as DWord))
  expectEqual(1, all1s << (-15 as Int))
  expectEqual(0, all1s << (-16 as Int32))
}

tests.test("SmartBitShift/Unconstrained/Left/UInt16") {
  let all1s = ~0 as UInt16
  expectEqual(all1s, all1s << 0)
  expectEqual(0b1111_1111_1111_1110, all1s << 1)
  expectEqual(UInt16.max / 2 + 1, all1s << 15)
  expectEqual(0, all1s << 16)

  expectEqual(UInt16.max / 2, all1s << -1)
  expectEqual(1, all1s << -15)
  expectEqual(0, all1s << -16)
}

tests.test("Basics") {
  typealias I8 = UInt8
  let b8: I8 = 0b1_0_11_0_111
  expectEqual(b8, 0b1_0_11_0_111)
  expectEqual(b8, 183)
  expectNotEqual(b8, I8())
  expectEqual(I8(), 0)
  expectEqual(8, I8.bitWidth)
  expectEqual(16, Int16.bitWidth)
  expectEqual(32, Int32.bitWidth)
}

tests.test("words") {
  expectEqualSequence([UInt.max], UInt.max.words)
  expectEqualSequence([0xFF as UInt], UInt8.max.words)
  expectEqualSequence([0xFFFF as UInt], UInt16.max.words)
  expectEqualSequence([0xFFFFFFFF as UInt], UInt32.max.words)

  expectEqualSequence([0 as UInt], UInt.min.words)
  expectEqualSequence([0 as UInt], UInt8.min.words)
  expectEqualSequence([0 as UInt], UInt16.min.words)
  expectEqualSequence([0 as UInt], UInt32.min.words)

  expectEqualSequence([UInt.max >> 1], Int.max.words)
  expectEqualSequence([0x7F as UInt], Int8.max.words)
  expectEqualSequence([0x7FFF as UInt], Int16.max.words)
  expectEqualSequence([0x7FFFFFFF as UInt], Int32.max.words)

  expectEqualSequence([UInt.max << (Int.bitWidth - 1)], Int.min.words)
  expectEqualSequence([UInt.max << 7], Int8.min.words)
  expectEqualSequence([UInt.max << 15], Int16.min.words)
  expectEqualSequence([UInt.max << 31], Int32.min.words)
  
  expectEqualSequence([UInt.max], (-1 as Int).words)
  expectEqualSequence([UInt.max], (-1 as Int8).words)
  expectEqualSequence([UInt.max], (-1 as Int16).words)
  expectEqualSequence([UInt.max], (-1 as Int32).words)

% if int(WORD_BITS) == 64:
  expectEqualSequence([UInt.max], UInt64.max.words)
  expectEqualSequence([0 as UInt], UInt64.min.words)
  expectEqualSequence([UInt.max >> 1], Int64.max.words)
  expectEqualSequence([(1 as UInt) << 63], Int64.min.words)
  expectEqualSequence([UInt.max], (-1 as Int64).words)
% else:
  expectEqualSequence([UInt.max, UInt.max], UInt64.max.words)
  expectEqualSequence([0 as UInt, 0], UInt64.min.words)
  expectEqualSequence([UInt.max, UInt.max >> 1], Int64.max.words)
  expectEqualSequence([0 as UInt, 1 << 31], Int64.min.words)
  expectEqualSequence([UInt.max, UInt.max], (-1 as Int64).words)
% end

  expectEqualSequence([1], 1.words)
  expectEqualSequence([0], 0.words)

  // Random access to words with Int indexing
  expectEqual(1, 1.words[0])
}

tests.test("multipliedFullWidth/UInt8") {
  let a: UInt8 = 42
  let b: UInt8 = 42
  let res = a.multipliedFullWidth(by: b)
  expectEqual(0x06, res.high)
  expectEqual(0xe4, res.low)
}

tests.test("multipliedFullWidth/Int8") {
  let a: Int8 = 42
  let b: Int8 = -42
  let res = a.multipliedFullWidth(by: b)
  expectEqual(Int8(bitPattern: 0xf9), res.high)
  expectEqual(0x1c, res.low)
}

tests.test("multipliedFullWidth/Int8/BothNegative") {
  let a: Int8 = -42
  let b: Int8 = -42
  let res = a.multipliedFullWidth(by: b)
  expectEqual(0x06, res.high)
  expectEqual(0xe4, res.low)
}

tests.test("MultiplyAndDivideFullWidth/Int8") {
  let a: Int8 = 42
  let b: Int8 = 43
  let res = a.multipliedFullWidth(by: b)
  let (quotient, remainder) = b.dividingFullWidth(res)
  expectEqual(a, quotient)
  expectEqual(0, remainder)
}

tests.test("Remainder/DividingBy0") {
  func f(_ x: Int, _ y: Int) -> Int {
    return x % y
  }
  expectCrashLater()
  _ = f(42, 0)
}

tests.test("RemainderReportingOverflow/DividingByMinusOne") {
  // Work around https://github.com/apple/swift/issues/48523.
  func minusOne<T : SignedInteger>() -> T {
    return -1 as T
  }
  func f(_ x: Int, _ y: Int) -> Int {
    return x.remainderReportingOverflow(dividingBy: y).partialValue
  }
  expectEqual(f(.max, minusOne()), 0)
  expectEqual(f(.min, minusOne()), 0)
}

tests.test("Division/By0") {
  func f(_ x: Int, _ y: Int) -> Int {
    return x / y
  }
  expectCrashLater()
  _ = f(42, 0)
}

tests.test("DivideMinByMinusOne") {
  func f(_ x: Int) -> Int {
    return x / -1
  }
  expectCrashLater()
  _ = f(Int.min)
}

tests.test("isMultiple") {
  func isMultipleTest<T: FixedWidthInteger>(type: T.Type) {
    expectTrue(T.min.isMultiple(of: 2))
    expectFalse(T.max.isMultiple(of: 10))
    // Test that these do not crash.
    expectTrue((0 as T).isMultiple(of: 0))
    expectFalse((1 as T).isMultiple(of: 0))
    expectTrue(T.min.isMultiple(of: 0 &- 1))
  }
  isMultipleTest(type: Int.self)
  isMultipleTest(type: Int8.self)
  isMultipleTest(type: Int16.self)
  isMultipleTest(type: Int32.self)
  isMultipleTest(type: Int64.self)
  isMultipleTest(type: UInt.self)
  isMultipleTest(type: UInt8.self)
  isMultipleTest(type: UInt16.self)
  isMultipleTest(type: UInt32.self)
  isMultipleTest(type: UInt64.self)
}

tests.test("MultiplyMinByMinusOne") {
  func f(_ x: Int) -> Int {
    return x * -1
  }
  expectCrashLater()
  _ = f(Int.min)
}

tests.test("Strideable") {
  func dist<T: BinaryInteger>(_ a: T, _ b: T) -> Int {
    return a.distance(to: b)
  }
% for u in ['U', '']:
  let ${u}x = ${u}Int.max - 10
  expectEqual(${u}x.advanced(by: 10), ${u}Int.max)
  expectEqual(${u}Int.max.advanced(by: -10), ${u}x)
  expectEqual(dist(${u}x, ${u}Int.max), 10)
  expectEqual(dist(${u}Int.max, ${u}x), -10)

  // FIXME: The compiler spuriously flags these as overflowing:
  // https://github.com/apple/swift/issues/48452
  // expectEqual(${u}x.distance(to: ${u}Int.max), 10)
% end

  expectEqual(dist(UInt8.min, UInt8.max), 255)
  expectEqual(dist(UInt8.max, UInt8.min), -255)
  expectEqual(dist(Int8.min, Int8.max), 255)
  expectEqual(dist(Int8.max, Int8.min), -255)

  if #available(SwiftStdlib 6.0, *) {
    // Edge cases fixed in Swift 6.0
    expectEqual(Int8.min.advanced(by: Int(Int8.max)+1), 0)
    expectEqual(UInt.max.advanced(by: Int.min), UInt.max / 2)
  }
}

tests.test("signum/generic") {
  func check<T : BinaryInteger>(_ expected: T, _ x: T) {
    expectEqual(expected, x.signum())
  }
% for suffix in ['8', '16', '32', '64', '']:
  check(-1, Int${suffix}.min)
  check(-1, (-42) as Int${suffix})
  check(-1, (-1) as Int${suffix})
%   for u in ['U', '']:
  check(0, 0 as ${u}Int${suffix})
  check(1, 1 as ${u}Int${suffix})
  check(1, 42 as ${u}Int${suffix})
  check(1, ${u}Int${suffix}.max)
%   end
% end
}

tests.test("signum/concrete") {
% for suffix in ['8', '16', '32', '64', '']:
  expectEqual(-1 as Int${suffix}, Int${suffix}.min.signum())
  expectEqual(-1 as Int${suffix}, (-42 as Int${suffix}).signum())
  expectEqual(-1 as Int${suffix}, (-1 as Int${suffix}).signum())
%   for u in ['U', '']:
  expectEqual(0 as ${u}Int${suffix}, (0 as ${u}Int${suffix}).signum())
  expectEqual(1 as ${u}Int${suffix}, (1 as ${u}Int${suffix}).signum())
  expectEqual(1 as ${u}Int${suffix}, (42 as ${u}Int${suffix}).signum())
  expectEqual(1 as ${u}Int${suffix}, ${u}Int${suffix}.max.signum())
%   end
% end
}

tests.test("binaryLogarithm/generic") {
  expectEqual(
    (42 as MockBinaryInteger<Int8>)._binaryLogarithm(),
    (42 as Int8)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<UInt8>)._binaryLogarithm(),
    (42 as UInt8)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<Int16>)._binaryLogarithm(),
    (42 as Int16)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<UInt16>)._binaryLogarithm(),
    (42 as UInt16)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<Int32>)._binaryLogarithm(),
    (42 as Int32)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<UInt32>)._binaryLogarithm(),
    (42 as UInt32)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<Int64>)._binaryLogarithm(),
    (42 as Int64)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<UInt64>)._binaryLogarithm(),
    (42 as UInt64)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<Int>)._binaryLogarithm(),
    (42 as Int)._binaryLogarithm())
  expectEqual(
    (42 as MockBinaryInteger<UInt>)._binaryLogarithm(),
    (42 as UInt)._binaryLogarithm())
}

runAllTests()
