import Swift
import Darwin
import StdlibUnittest
import Foundation

func convertDictionaryToNSDictionary<Key, Value>(
  _ d: [Key : Value]
) -> NSDictionary {
  return d._bridgeToObjectiveC()
}

public func convertNSDictionaryToDictionary<
  Key : Hashable, Value
>(_ d: NSDictionary?) -> [Key : Value] {
  if _slowPath(d == nil) { return [:] }
  var result: [Key : Value]?
  Dictionary._forceBridgeFromObjectiveC(d!, result: &result)
  return result!
}

func isNativeDictionary<KeyTy : Hashable, ValueTy>(
  _ d: Dictionary<KeyTy, ValueTy>) -> Bool {
  switch d._variantBuffer {
  case .native:
    return true
  case .cocoa:
    return false
  }
}

func isCocoaDictionary<KeyTy : Hashable, ValueTy>(
  _ d: Dictionary<KeyTy, ValueTy>) -> Bool {
  return !isNativeDictionary(d)
}

func isNativeNSDictionary(_ d: NSDictionary) -> Bool {
  let className: NSString = NSStringFromClass(type(of: d)) as NSString
  return ["_SwiftDeferredNSDictionary", "NativeDictionaryStorage"].contains {
    className.range(of: $0).length > 0
  }
}

func isCocoaNSDictionary(_ d: NSDictionary) -> Bool {
  let className: NSString = NSStringFromClass(type(of: d)) as NSString
  return className.range(of: "NSDictionary").length > 0 ||
    className.range(of: "NSCFDictionary").length > 0
}

func isNativeNSArray(_ d: NSArray) -> Bool {
  let className: NSString = NSStringFromClass(type(of: d)) as NSString
  return ["_SwiftDeferredNSArray", "_ContiguousArray", "_EmptyArray"].contains {
    className.range(of: $0).length > 0
  }
}

var _objcKeyCount = _stdlib_AtomicInt(0)
var _objcKeySerial = _stdlib_AtomicInt(0)

class TestObjCKeyTy : NSObject, NSCopying {
  class var objectCount: Int {
    get {
      return _objcKeyCount.load()
    }
    set {
      _objcKeyCount.store(newValue)
    }
  }

  init(_ value: Int) {
    _objcKeyCount.fetchAndAdd(1)
    serial = _objcKeySerial.addAndFetch(1)
    self.value = value
    self._hashValue = value
    super.init()
  }

  convenience init(value: Int, hashValue: Int) {
    self.init(value)
    self._hashValue = hashValue
  }

  deinit {
    assert(serial > 0, "double destruction")
    _objcKeyCount.fetchAndAdd(-1)
    serial = -serial
  }

  @objc(copyWithZone:)
  func copy(with zone: NSZone?) -> Any {
    return TestObjCKeyTy(value)
  }

  override var description: String {
    assert(serial > 0, "dead TestObjCKeyTy")
    return value.description
  }

  override func isEqual(_ object: Any!) -> Bool {
    if let other = object {
      if let otherObjcKey = other as? TestObjCKeyTy {
        return self.value == otherObjcKey.value
      }
    }
    return false
  }

  override var hash : Int {
    return _hashValue
  }

  func _bridgeToObjectiveC() -> TestObjCKeyTy {
    return self
  }

  var value: Int
  var _hashValue: Int
  var serial: Int
}

// A type that satisfies the requirements of an NSDictionary key (or an NSSet
// member), but traps when any of its methods are called.
class TestObjCInvalidKeyTy {
  init() {
    _objcKeyCount.fetchAndAdd(1)
    serial = _objcKeySerial.addAndFetch(1)
  }

  deinit {
    assert(serial > 0, "double destruction")
    _objcKeyCount.fetchAndAdd(-1)
    serial = -serial
  }

  @objc
  var description: String {
    assert(serial > 0, "dead TestObjCInvalidKeyTy")
    fatalError()
  }

  @objc
  func isEqual(_ object: Any!) -> Bool {
    fatalError()
  }

  @objc
  var hash : Int {
    fatalError()
  }

  var serial: Int
}

var _objcValueCount = _stdlib_AtomicInt(0)
var _objcValueSerial = _stdlib_AtomicInt(0)

class TestObjCValueTy : NSObject {
  class var objectCount: Int {
    get {
      return _objcValueCount.load()
    }
    set {
      _objcValueCount.store(newValue)
    }
  }

  init(_ value: Int) {
    _objcValueCount.fetchAndAdd(1)
    serial = _objcValueSerial.addAndFetch(1)
    self.value = value
  }

  deinit {
    assert(serial > 0, "double destruction")
    _objcValueCount.fetchAndAdd(-1)
    serial = -serial
  }

  override var description: String {
    assert(serial > 0, "dead TestObjCValueTy")
    return value.description
  }

  var value: Int
  var serial: Int
}

var _objcEquatableValueCount = _stdlib_AtomicInt(0)
var _objcEquatableValueSerial = _stdlib_AtomicInt(0)

class TestObjCEquatableValueTy : NSObject {
  class var objectCount: Int {
    get {
      return _objcEquatableValueCount.load()
    }
    set {
      _objcEquatableValueCount.store(newValue)
    }
  }

  init(_ value: Int) {
    _objcEquatableValueCount.fetchAndAdd(1)
    serial = _objcEquatableValueSerial.addAndFetch(1)
    self.value = value
  }

  deinit {
    assert(serial > 0, "double destruction")
    _objcEquatableValueCount.fetchAndAdd(-1)
    serial = -serial
  }

  override func isEqual(_ object: Any!) -> Bool {
    if let other = object {
      if let otherObjcKey = other as? TestObjCEquatableValueTy {
        return self.value == otherObjcKey.value
      }
    }
    return false
  }

  override var description: String {
    assert(serial > 0, "dead TestObjCValueTy")
    return value.description
  }

  var value: Int
  var serial: Int
}

func == (lhs: TestObjCEquatableValueTy, rhs: TestObjCEquatableValueTy) -> Bool {
  return lhs.value == rhs.value
}

var _bridgedKeySerial = _stdlib_AtomicInt(0)
var _bridgedKeyBridgeOperations = _stdlib_AtomicInt(0)

struct TestBridgedKeyTy
  : Equatable, Hashable, CustomStringConvertible, _ObjectiveCBridgeable {
  static var bridgeOperations: Int {
    get {
      return _bridgedKeyBridgeOperations.load()
    }
    set {
      _bridgedKeyBridgeOperations.store(newValue)
    }
  }

  init(_ value: Int) {
    serial = _bridgedKeySerial.addAndFetch(1)
    self.value = value
    self._hashValue = value
  }

  var description: String {
    assert(serial > 0, "dead TestBridgedKeyTy")
    return value.description
  }

  var hashValue: Int {
    return _hashValue
  }

  func _bridgeToObjectiveC() -> TestObjCKeyTy {
    _bridgedKeyBridgeOperations.fetchAndAdd(1)
    return TestObjCKeyTy(value)
  }

  static func _forceBridgeFromObjectiveC(
    _ x: TestObjCKeyTy,
    result: inout TestBridgedKeyTy?
  ) {
    _bridgedKeyBridgeOperations.fetchAndAdd(1)
    result = TestBridgedKeyTy(x.value)
  }

  static func _conditionallyBridgeFromObjectiveC(
    _ x: TestObjCKeyTy,
    result: inout TestBridgedKeyTy?
  ) -> Bool {
    self._forceBridgeFromObjectiveC(x, result: &result)
    return true
  }

  static func _unconditionallyBridgeFromObjectiveC(_ source: TestObjCKeyTy?)
      -> TestBridgedKeyTy {
    var result: TestBridgedKeyTy? = nil
    _forceBridgeFromObjectiveC(source!, result: &result)
    return result!
  }

  var value: Int
  var _hashValue: Int
  var serial: Int
}

func == (lhs: TestBridgedKeyTy, rhs: TestBridgedKeyTy) -> Bool {
  return lhs.value == rhs.value
}

func == (lhs: TestBridgedKeyTy, rhs: TestKeyTy) -> Bool {
  return lhs.value == rhs.value
}

var _bridgedValueSerial = _stdlib_AtomicInt(0)
var _bridgedValueBridgeOperations = _stdlib_AtomicInt(0)

struct TestBridgedValueTy : CustomStringConvertible, _ObjectiveCBridgeable {
  static var bridgeOperations: Int {
    get {
      return _bridgedValueBridgeOperations.load()
    }
    set {
      _bridgedValueBridgeOperations.store(newValue)
    }
  }

  init(_ value: Int) {
    serial = _bridgedValueSerial.fetchAndAdd(1)
    self.value = value
  }

  var description: String {
    assert(serial > 0, "dead TestBridgedValueTy")
    return value.description
  }

  func _bridgeToObjectiveC() -> TestObjCValueTy {
    TestBridgedValueTy.bridgeOperations += 1
    return TestObjCValueTy(value)
  }

  static func _forceBridgeFromObjectiveC(
    _ x: TestObjCValueTy,
    result: inout TestBridgedValueTy?
  ) {
    TestBridgedValueTy.bridgeOperations += 1
    result = TestBridgedValueTy(x.value)
  }

  static func _conditionallyBridgeFromObjectiveC(
    _ x: TestObjCValueTy,
    result: inout TestBridgedValueTy?
  ) -> Bool {
    self._forceBridgeFromObjectiveC(x, result: &result)
    return true
  }

  static func _unconditionallyBridgeFromObjectiveC(_ source: TestObjCValueTy?)
      -> TestBridgedValueTy {
    var result: TestBridgedValueTy? = nil
    _forceBridgeFromObjectiveC(source!, result: &result)
    return result!
  }

  var value: Int
  var serial: Int
}

var _bridgedEquatableValueSerial = _stdlib_AtomicInt(0)
var _bridgedEquatableValueBridgeOperations = _stdlib_AtomicInt(0)

struct TestBridgedEquatableValueTy
  : Equatable, CustomStringConvertible, _ObjectiveCBridgeable {

  static var bridgeOperations: Int {
    get {
      return _bridgedEquatableValueBridgeOperations.load()
    }
    set {
      _bridgedEquatableValueBridgeOperations.store(newValue)
    }
  }

  init(_ value: Int) {
    serial = _bridgedEquatableValueSerial.addAndFetch(1)
    self.value = value
  }

  var description: String {
    assert(serial > 0, "dead TestBridgedValueTy")
    return value.description
  }

  func _bridgeToObjectiveC() -> TestObjCEquatableValueTy {
    _bridgedEquatableValueBridgeOperations.fetchAndAdd(1)
    return TestObjCEquatableValueTy(value)
  }

  static func _forceBridgeFromObjectiveC(
    _ x: TestObjCEquatableValueTy,
    result: inout TestBridgedEquatableValueTy?
  ) {
    _bridgedEquatableValueBridgeOperations.fetchAndAdd(1)
    result = TestBridgedEquatableValueTy(x.value)
  }

  static func _conditionallyBridgeFromObjectiveC(
    _ x: TestObjCEquatableValueTy,
    result: inout TestBridgedEquatableValueTy?
  ) -> Bool {
    self._forceBridgeFromObjectiveC(x, result: &result)
    return true
  }

  static func _unconditionallyBridgeFromObjectiveC(
    _ source: TestObjCEquatableValueTy?
  ) -> TestBridgedEquatableValueTy {
    var result: TestBridgedEquatableValueTy? = nil
    _forceBridgeFromObjectiveC(source!, result: &result)
    return result!
  }

  var value: Int
  var serial: Int
}

func == (lhs: TestBridgedEquatableValueTy, rhs: TestBridgedEquatableValueTy) -> Bool {
  return lhs.value == rhs.value
}

/// Expect some number of autoreleased key and value objects.
///
/// - parameter opt: applies to platforms that have the return-autoreleased
///   optimization.
///
/// - parameter unopt: applies to platforms that don't.
///
/// FIXME: Some non-zero `opt` might be cases of missed return-autorelease.
func expectAutoreleasedKeysAndValues(
  opt: (Int, Int) = (0, 0), unopt: (Int, Int) = (0, 0)) {
  var expectedKeys = 0
  var expectedValues = 0
#if arch(i386)
  (expectedKeys, expectedValues) = unopt
#else
  (expectedKeys, expectedValues) = opt
#endif

  TestObjCKeyTy.objectCount -= expectedKeys
  TestObjCValueTy.objectCount -= expectedValues
}

/// Expect some number of autoreleased value objects.
///
/// - parameter opt: applies to platforms that have the return-autoreleased
///   optimization.
///
/// - parameter unopt: applies to platforms that don't.
///
/// FIXME: Some non-zero `opt` might be cases of missed return-autorelease.
func expectAutoreleasedValues(
  opt: Int = 0, unopt: Int = 0) {
  expectAutoreleasedKeysAndValues(opt: (0, opt), unopt: (0, unopt))
}

func resetLeaksOfObjCDictionaryKeysValues() {
  TestObjCKeyTy.objectCount = 0
  TestObjCValueTy.objectCount = 0
  TestObjCEquatableValueTy.objectCount = 0
}

func expectNoLeaksOfObjCDictionaryKeysValues() {
  expectEqual(0, TestObjCKeyTy.objectCount, "TestObjCKeyTy leak")
  expectEqual(0, TestObjCValueTy.objectCount, "TestObjCValueTy leak")
  expectEqual(
    0, TestObjCEquatableValueTy.objectCount, "TestObjCEquatableValueTy leak")
}

func getBridgedNSDictionaryOfRefTypesBridgedVerbatim() -> NSDictionary {
  assert(_isBridgedVerbatimToObjectiveC(TestObjCKeyTy.self))
  assert(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self))

  var d = Dictionary<TestObjCKeyTy, TestObjCValueTy>(minimumCapacity: 32)
  d[TestObjCKeyTy(10)] = TestObjCValueTy(1010)
  d[TestObjCKeyTy(20)] = TestObjCValueTy(1020)
  d[TestObjCKeyTy(30)] = TestObjCValueTy(1030)

  let bridged =
    unsafeBitCast(convertDictionaryToNSDictionary(d), to: NSDictionary.self)

  assert(isNativeNSDictionary(bridged))

  return bridged
}

func getBridgedEmptyNSDictionary() -> NSDictionary {
  let d = Dictionary<TestObjCKeyTy, TestObjCValueTy>()

  let bridged =
    unsafeBitCast(convertDictionaryToNSDictionary(d), to: NSDictionary.self)
  assert(isNativeNSDictionary(bridged))

  return bridged
}

func getBridgedNSDictionaryOfKeyValue_ValueTypesCustomBridged(
  numElements: Int = 3
) -> NSDictionary {
  assert(!_isBridgedVerbatimToObjectiveC(TestBridgedKeyTy.self))
  assert(!_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self))

  var d = Dictionary<TestBridgedKeyTy, TestBridgedValueTy>()
  for i in 1..<(numElements + 1) {
    d[TestBridgedKeyTy(i * 10)] = TestBridgedValueTy(i * 10 + 1000)
  }

  let bridged = convertDictionaryToNSDictionary(d)
  assert(isNativeNSDictionary(bridged))

  return bridged
}

import SlurpFastEnumeration

 func slurpFastEnumerationFromSwift(
  _ a: NSArray, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void,
  maxItems: Int? = nil
) {
  var state = NSFastEnumerationState()

  let stackBufLength = 3
  let stackBuf = _HeapBuffer<(), AnyObject?>(
    _HeapBufferStorage<(), AnyObject?>.self, (), stackBufLength)

  var itemsReturned = 0
  while true {
    let returnedCount = fe.countByEnumerating(
      with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress),
      count: stackBufLength)
    expectNotEqual(0, state.state)
    expectNotNil(state.mutationsPtr)
    if returnedCount == 0 {
      break
    }
    for i in 0..<returnedCount {
      let value: AnyObject = state.itemsPtr![i]!
      sink(value)
      itemsReturned += 1
    }
    if maxItems != nil && itemsReturned >= maxItems! {
      return
    }
  }

  for _ in 0..<3 {
    let returnedCount = fe.countByEnumerating(
      with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress),
      count: stackBufLength)
    expectNotEqual(0, state.state)
    expectNotNil(state.mutationsPtr)
    expectEqual(0, returnedCount)
  }
}

typealias AnyObjectTuple2 = (AnyObject, AnyObject)

 func slurpFastEnumerationFromSwift(
  _ d: NSDictionary, _ fe: NSFastEnumeration, _ sink: (AnyObjectTuple2) -> Void,
  maxItems: Int? = nil
) {
  var state = NSFastEnumerationState()

  let stackBufLength = 3
  let stackBuf = _HeapBuffer<(), AnyObject?>(
    _HeapBufferStorage<(), AnyObject?>.self, (), stackBufLength)

  var itemsReturned = 0
  while true {
    let returnedCount = fe.countByEnumerating(
      with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress),
      count: stackBufLength)
    expectNotEqual(0, state.state)
    expectNotNil(state.mutationsPtr)
    if returnedCount == 0 {
      break
    }
    for i in 0..<returnedCount {
      let key: AnyObject = state.itemsPtr![i]!
      let value: AnyObject = d.object(forKey: key)! as AnyObject
      let kv = (key, value)
      sink(kv)
      itemsReturned += 1
    }
    if maxItems != nil && itemsReturned >= maxItems! {
      return
    }
  }

  for _ in 0..<3 {
    let returnedCount = fe.countByEnumerating(
      with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress),
      count: stackBufLength)
    expectEqual(0, returnedCount)
  }
}

 func slurpFastEnumerationOfNSEnumeratorFromSwift(
  _ a: NSArray, _ enumerator: NSEnumerator, _ sink: (AnyObject) -> Void,
  maxFastEnumerationItems: Int
) {
  slurpFastEnumerationFromSwift(
    a, enumerator, sink, maxItems: maxFastEnumerationItems)
  while let value = enumerator.nextObject() {
    sink(value as AnyObject)
  }
}

 func slurpFastEnumerationOfNSEnumeratorFromSwift(
  _ d: NSDictionary, _ enumerator: NSEnumerator,
  _ sink: (AnyObjectTuple2) -> Void,
  maxFastEnumerationItems: Int
) {
  slurpFastEnumerationFromSwift(
    d, enumerator, sink, maxItems: maxFastEnumerationItems)
  while let key = enumerator.nextObject() {
    let value: AnyObject = d.object(forKey: key)! as AnyObject
    let kv = (key as AnyObject, value)
    sink(kv)
  }
}

 func slurpFastEnumerationFromObjC(
  _ a: NSArray, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void
) {
  let objcValues = NSMutableArray()
  slurpFastEnumerationOfArrayFromObjCImpl(a, fe, objcValues)
  for value in objcValues {
    sink(value as AnyObject)
  }
}

 func _checkArrayFastEnumerationImpl(
  _ expected: [Int],
  _ a: NSArray,
  _ makeEnumerator: () -> NSFastEnumeration,
  _ useEnumerator: (NSArray, NSFastEnumeration, (AnyObject) -> ()) -> Void,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  let expectedContentsWithoutIdentity =
  _makeExpectedArrayContents(expected)
  
  var expectedContents = [ExpectedArrayElement]()
  
  for i in 0..<3 {
    var actualContents = [ExpectedArrayElement]()
    let sink = {
      (value: AnyObject) in
      actualContents.append(ExpectedArrayElement(
        value: convertValue(value),
        valueIdentity: unsafeBitCast(value, to: UInt.self)))
    }

    useEnumerator(a, makeEnumerator(), sink)

    expectTrue(
      _equalsWithoutElementIdentity(
        expectedContentsWithoutIdentity, actualContents),
      "expected: \(expectedContentsWithoutIdentity)\n" +
      "actual: \(actualContents)\n")

    if i == 0 {
      expectedContents = actualContents
    }
    
    expectEqualSequence(expectedContents, actualContents)
  }
}

 func checkArrayFastEnumerationFromSwift(
  _ expected: [Int],
  _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  _checkArrayFastEnumerationImpl(
    expected, a, makeEnumerator,
    { (a, fe, sink) in
      slurpFastEnumerationFromSwift(a, fe, sink)
    },
    convertValue)
}

 func checkArrayFastEnumerationFromObjC(
  _ expected: [Int],
  _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  _checkArrayFastEnumerationImpl(
    expected, a, makeEnumerator,
    { (a, fe, sink) in
      slurpFastEnumerationFromObjC(a, fe, sink)
    },
    convertValue)
}

 func checkArrayEnumeratorPartialFastEnumerationFromSwift(
  _ expected: [Int],
  _ a: NSArray,
  maxFastEnumerationItems: Int,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  _checkArrayFastEnumerationImpl(
    expected, a, { a.objectEnumerator() },
    { (a, fe, sink) in
      slurpFastEnumerationOfNSEnumeratorFromSwift(
        a, fe as! NSEnumerator, sink,
        maxFastEnumerationItems: maxFastEnumerationItems)
    },
    convertValue)
}

 func _checkSetFastEnumerationImpl(
  _ expected: [Int],
  _ s: NSSet,
  _ makeEnumerator: () -> NSFastEnumeration,
  _ useEnumerator: (NSSet, NSFastEnumeration, (AnyObject) -> ()) -> Void,
  _ convertMember: @escaping (AnyObject) -> Int
) {
  let expectedContentsWithoutIdentity =
    _makeExpectedSetContents(expected)
  var expectedContents = [ExpectedSetElement]()

  for i in 0..<3 {
    var actualContents = [ExpectedSetElement]()
    let sink = {
      (value: AnyObject) in
      actualContents.append(ExpectedSetElement(
        value: convertMember(value),
        valueIdentity: unsafeBitCast(value, to: UInt.self)))
    }

    useEnumerator(s, makeEnumerator(), sink)

    expectTrue(
      _equalsUnorderedWithoutElementIdentity(
        expectedContentsWithoutIdentity, actualContents),
      "expected: \(expectedContentsWithoutIdentity)\n" +
      "actual: \(actualContents)\n")

    if i == 0 {
      expectedContents = actualContents
    }
    expectTrue(equalsUnordered(expectedContents, actualContents))
  }
}

 func slurpFastEnumerationFromObjC(
  _ s: NSSet, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void
) {
  let objcValues = NSMutableArray()
  slurpFastEnumerationOfArrayFromObjCImpl(s, fe, objcValues)
  for value in objcValues {
    sink(value as AnyObject)
  }
}

 func slurpFastEnumerationOfNSEnumeratorFromSwift(
  _ s: NSSet, _ enumerator: NSEnumerator, _ sink: (AnyObject) -> Void,
  maxFastEnumerationItems: Int
) {
  slurpFastEnumerationFromSwift(
    s, enumerator, sink, maxItems: maxFastEnumerationItems)
  while let value = enumerator.nextObject() {
    sink(value as AnyObject)
  }
}

 func slurpFastEnumerationFromSwift(
  _ s: NSSet, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void,
  maxItems: Int? = nil
) {
  var state = NSFastEnumerationState()

  let stackBufLength = 3
  let stackBuf = _HeapBuffer<(), AnyObject?>(
    _HeapBufferStorage<(), AnyObject?>.self, (), stackBufLength)

  var itemsReturned = 0
  while true {
    let returnedCount = fe.countByEnumerating(
      with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress),
      count: stackBufLength)
    expectNotEqual(0, state.state)
    expectNotNil(state.mutationsPtr)
    if returnedCount == 0 {
      break
    }
    for i in 0..<returnedCount {
      let value: AnyObject = state.itemsPtr![i]!
      sink(value)
      itemsReturned += 1
    }
    if maxItems != nil && itemsReturned >= maxItems! {
      return
    }
  }

  for _ in 0..<3 {
    let returnedCount = fe.countByEnumerating(
      with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress),
      count: stackBufLength)
    expectNotEqual(0, state.state)
    expectNotNil(state.mutationsPtr)
    expectEqual(0, returnedCount)
  }
}

 func checkSetFastEnumerationFromSwift(
  _ expected: [Int],
  _ s: NSSet, _ makeEnumerator: () -> NSFastEnumeration,
  _ convertMember: @escaping (AnyObject) -> Int
) {
  _checkSetFastEnumerationImpl(
    expected, s, makeEnumerator,
    { (s, fe, sink) in
      slurpFastEnumerationFromSwift(s, fe, sink)
    },
    convertMember)
}

 func checkSetFastEnumerationFromObjC(
  _ expected: [Int],
  _ s: NSSet, _ makeEnumerator: () -> NSFastEnumeration,
  _ convertMember: @escaping (AnyObject) -> Int
) {
  _checkSetFastEnumerationImpl(
    expected, s, makeEnumerator,
    { (s, fe, sink) in
      slurpFastEnumerationFromObjC(s, fe, sink)
    },
    convertMember)
}

 func checkSetEnumeratorPartialFastEnumerationFromSwift(
  _ expected: [Int],
  _ s: NSSet,
  maxFastEnumerationItems: Int,
  _ convertMember: @escaping (AnyObject) -> Int
) {
  _checkSetFastEnumerationImpl(
    expected, s, { s.objectEnumerator() },
    { (s, fe, sink) in
      slurpFastEnumerationOfNSEnumeratorFromSwift(
        s, fe as! NSEnumerator, sink,
        maxFastEnumerationItems: maxFastEnumerationItems)
    },
    convertMember)
}

 func slurpFastEnumerationFromObjC(
  _ d: NSDictionary, _ fe: NSFastEnumeration, _ sink: (AnyObjectTuple2) -> Void
) {
  let objcPairs = NSMutableArray()
  slurpFastEnumerationOfDictionaryFromObjCImpl(d, fe, objcPairs)
  for i in 0..<objcPairs.count/2 {
    let key = objcPairs[i * 2] as AnyObject
    let value = objcPairs[i * 2 + 1] as AnyObject
    let kv = (key, value)
    sink(kv)
  }
}

 func _checkDictionaryFastEnumerationImpl(
  _ expected: [(Int, Int)],
  _ d: NSDictionary,
  _ makeEnumerator: () -> NSFastEnumeration,
  _ useEnumerator: (NSDictionary, NSFastEnumeration, (AnyObjectTuple2) -> ()) -> Void,
  _ convertKey: @escaping (AnyObject) -> Int,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  let expectedContentsWithoutIdentity =
    _makeExpectedDictionaryContents(expected)
  var expectedContents = [ExpectedDictionaryElement]()

  for i in 0..<3 {
    var actualContents = [ExpectedDictionaryElement]()
    let sink: (AnyObjectTuple2) -> Void = {
      (key, value) in
      actualContents.append(ExpectedDictionaryElement(
        key: convertKey(key),
        value: convertValue(value),
        keyIdentity: unsafeBitCast(key, to: UInt.self),
        valueIdentity: unsafeBitCast(value, to: UInt.self)))
    }

    useEnumerator(d, makeEnumerator(), sink)

    expectTrue(
      _equalsUnorderedWithoutElementIdentity(
        expectedContentsWithoutIdentity, actualContents),
      "expected: \(expectedContentsWithoutIdentity)\n" +
      "actual: \(actualContents)\n")

    if i == 0 {
      expectedContents = actualContents
    }
    expectTrue(equalsUnordered(expectedContents, actualContents))
  }
}

 func checkDictionaryFastEnumerationFromSwift(
  _ expected: [(Int, Int)],
  _ d: NSDictionary, _ makeEnumerator: () -> NSFastEnumeration,
  _ convertKey: @escaping (AnyObject) -> Int,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  _checkDictionaryFastEnumerationImpl(
    expected, d, makeEnumerator,
    { (d, fe, sink) in
      slurpFastEnumerationFromSwift(d, fe, sink)
    },
    convertKey, convertValue)
}

 func checkDictionaryFastEnumerationFromObjC(
  _ expected: [(Int, Int)],
  _ d: NSDictionary, _ makeEnumerator: () -> NSFastEnumeration,
  _ convertKey: @escaping (AnyObject) -> Int,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  _checkDictionaryFastEnumerationImpl(
    expected, d, makeEnumerator,
    { (d, fe, sink) in
      slurpFastEnumerationFromObjC(d, fe, sink)
    },
    convertKey, convertValue)
}

 func checkDictionaryEnumeratorPartialFastEnumerationFromSwift(
  _ expected: [(Int, Int)],
  _ d: NSDictionary,
  maxFastEnumerationItems: Int,
  _ convertKey: @escaping (AnyObject) -> Int,
  _ convertValue: @escaping (AnyObject) -> Int
) {
  _checkDictionaryFastEnumerationImpl(
    expected, d, { d.keyEnumerator() },
    { (d, fe, sink) in
      slurpFastEnumerationOfNSEnumeratorFromSwift(
        d, fe as! NSEnumerator, sink,
        maxFastEnumerationItems: maxFastEnumerationItems)
    },
    convertKey, convertValue)
}

func getBridgedNSArrayOfRefTypeVerbatimBridged(
  numElements: Int = 3,
  capacity: Int? = nil
) -> NSArray {
  assert(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self))

  var a = [TestObjCValueTy]()
  if let requestedCapacity = capacity {
    a.reserveCapacity(requestedCapacity)
  }
  for i in 1..<(numElements + 1) {
    a.append(TestObjCValueTy(i * 10))
  }

  let bridged = convertArrayToNSArray(a)
  assert(isNativeNSArray(bridged))

  return bridged
}

func convertNSArrayToArray<T>(_ source: NSArray?) -> [T] {
  if _slowPath(source == nil) { return [] }
  var result: [T]?
  Array._forceBridgeFromObjectiveC(source!, result: &result)
  return result!
}

func convertArrayToNSArray<T>(_ array: [T]) -> NSArray {
  return array._bridgeToObjectiveC()
}

func getBridgedNSArrayOfValueTypeCustomBridged(
  numElements: Int = 3,
  capacity: Int? = nil
) -> NSArray {
  assert(!_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self))

  var a = [TestBridgedValueTy]()
  if let requestedCapacity = capacity {
    a.reserveCapacity(requestedCapacity)
  }
  for i in 1..<(numElements + 1) {
    a.append(TestBridgedValueTy(i * 10))
  }

  let bridged = convertArrayToNSArray(a)
  assert(isNativeNSArray(bridged))

  return bridged
}
