 
     /// The number of CPU devices.
     static public var cpuDeviceCount: UInt32 = 1
-
-    /// When non-nil, run metadata (with full trace) of each session execution will be dumped to the
-    /// give path.
-    static public var runMetadataOutputPath: String? = nil
 
     /// Specifies whether the TensorFlow computation runs in a local (in-process) session, or a
     /// remote session with the specified server definition.

        return _ThreadLocalState.useLazyTensor
            ? LazyTensorOperation(name, outputCount)
            : TFE_Op(name, outputCount)
    }
}

// Elements in `outputs` can come from two sources:
// a) Symbolic tensors produced by tensor ops, and added as trace graph nodes.
// b) Concrete tensors produced by host code (e.g. Tensor(1.0)).
fileprivate func finalizeTraceFunction(_ name: String) -> TraceContext {
    guard let traceContext = _RuntimeConfig.traceState.context else {
        fatalError("Not in tracing mode!.")
    }
    _RuntimeConfig.traceState = .notTracing
    traceContext.finalize(traceeBasicName: name)
    return traceContext
}

private extension TensorArrayProtocol {
    // The returned handles are owned by the caller.
    var cTensorHandles: [CTensorHandle] {
        debugLog("Getting \(self._tensorHandleCount) C handles.")
        let buffer = UnsafeMutablePointer<CTensorHandle>.allocate(
            capacity: Int(self._tensorHandleCount))
        debugLog("Unpacking handles into buffer.")
        _unpackTensorHandles(into: buffer)
        let status = TF_NewStatus()
        debugLog("Copying buffer content to output handles.")
        var output: [CTensorHandle] = []
        for i in 0..<Int(_tensorHandleCount) {
            let address = buffer.advanced(by: i)
            let isConcrete = TFE_TensorHandleIsConcrete(address.pointee) != 0
            debugLog("""
                     Copying the \(i)-th C handle \(address.pointee) with \
                     concrete=\(isConcrete).
                     """)
            let newHandle = TFE_TensorHandleCopySharingTensor(address.pointee, status)
            checkOk(status)
            internalConsistencyCheck(newHandle != nil)
            debugLog("""
                     Copying the \(i)-th C handle \(address.pointee) with \
                     concrete=\(isConcrete) to C handle create \(newHandle!).
                     """)
            output.append(newHandle!)
        }
        TF_DeleteStatus(status)
        return output
    }
}

private extension TensorGroup {
    // The tensors in `input` are NOT owned by this instance.
    init<C: Collection>(_copying input: C) where C.Element == CTensorHandle {
        assert(Self._tensorHandleCount == input.count)
        let buffer = UnsafeMutablePointer<CTensorHandle>.allocate(capacity: input.count)
        let status = TF_NewStatus()
        // copy input to buffer
        for (i, inputTensorHandle) in input.enumerated() {
            let address = buffer.advanced(by: i)
            // Each tensor can be symbolic (e.g. when using this API to create a symbolic input
            // instance to tracee) or concrete (e.g. when creating the final output of the tracee).
            let newHandle = TFE_TensorHandleCopySharingTensor(inputTensorHandle, status)
            checkOk(status)
            address.initialize(to: newHandle!)
        }
        TF_DeleteStatus(status)
        self.init(_owning: buffer)
    }

    init<C: Collection>(_owning input: C) where C.Element == CTensorHandle {
        assert(Self._tensorHandleCount == input.count)
        let buffer = UnsafeMutablePointer<CTensorHandle>.allocate(capacity: input.count)
        let status = TF_NewStatus()
        // copy input to buffer
        for (i, inputTensorHandle) in input.enumerated() {
            let address = buffer.advanced(by: i)
            address.initialize(to: inputTensorHandle)
        }
        TF_DeleteStatus(status)
        self.init(_owning: buffer)
    }
}

// TODO: Fold this protocol into TensorArrayProtocol.
// This requires that we move concrete implementation such as Tensor._makeInstance() to
// TensorGroup.swift.
public protocol _TensorArrayProtocolEnhanced: TensorArrayProtocol {
    // Create an instance based on `inputs`, which can be symbolic (e.g., when creating a symbolic
    // input to tracee) or concrete (e.., when creating a final output of executing the tracee).
    func _makeInstance<C: Collection>(owning inputs: C) -> Self where C.Element == CTensorHandle
}

extension _TensorArrayProtocolEnhanced {
    var _dtypes: [TF_DataType] {
        let count = Int(_tensorHandleCount)
        let buffer = UnsafeMutableBufferPointer<CTensorHandle>.allocate(capacity: count)
        defer { buffer.deallocate() }
        _unpackTensorHandles(into: buffer.baseAddress)
        return buffer.map { TFE_TensorHandleDataType($0) }
    }
}

/// Trace `fn` with newly created tensor handles and return a trace context.
private func _trace(
    with dtypes: [TF_DataType],
    in fn: ([CTensorHandle]) -> [CTensorHandle]
) -> TraceContext {
    debugLog("""
             Tracing over a function with \(dtypes.count) inputs.
             """)

    // Verify that we are not already tracing.
    internalConsistencyCheck(_RuntimeConfig.traceState.context == nil,
                             "Should not be in tracing mode already!")

    // Switch to tracing mode.
    let traceCtx = TraceContext(dtypes: dtypes)
    _RuntimeConfig.traceState = .tracing(traceCtx)

    // Handle inputs.
    let inputSymbolicTensors = traceCtx.symbolicInputs.map {
        TFE_NewTensorHandleFromTFOutput($0, TF_OperationOutputType($0))!
    }
    internalConsistencyCheck(inputSymbolicTensors.count == dtypes.count)

    // Run tracee to build the trace, adding ops to the trace graph function. The tracee output can
    // contain a mixture of symbolic and concrete tensors (see the comment block within
    // TraceContext.finalize()).
    debugLog("Running tracee in tracing mode.")
    traceCtx.tracedOutputs = fn(inputSymbolicTensors)

    debugLog("Finalizing trace graph function.")
    // TAP means tensor array protocol.
    let opType = "MyTraceFn_TAP"
    return finalizeTraceFunction(opType)
}

private func _graphInternal<
    State: _TensorArrayProtocolEnhanced,
    Data: TensorGroup,
    Result: TensorGroup
>(
    with state: State,
    in fn: (State, Data) -> (State, Result?)
) -> (State, Data) -> (State, Result?) {
    let traceContext: TraceContext = withoutActuallyEscaping(fn) { escapableFn in
        let wrappedFn = { (inputs: [CTensorHandle]) -> [CTensorHandle] in
            let symbolicState = state._makeInstance(owning: inputs.dropLast(Data._typeList.count))
            let symbolicData = Data(_copying: inputs.dropFirst(Int(state._tensorHandleCount)))
            let (outputState, outputResult) = escapableFn(symbolicState, symbolicData)

            debugLog("Assembling output tensor handles.")
            let outputs =
                outputResult != nil
                ? (outputState.cTensorHandles + outputResult!.cTensorHandles)
                : outputState.cTensorHandles
            return outputs
        }
        let dtypes = state._dtypes + Data._typeList.map { $0._cDataType }
        return _trace(with: dtypes, in: wrappedFn)
    }
    // The result is a closure that captures and executes the trace graph function in the trace
    // context.
    return { (oldState: State, data: Data) -> (State, Result?) in
        debugLog("Running trace function over state \(oldState) and data \(data).")

        debugLog("Getting input state tensor handles.")
        let inputStateTensorHandles =  oldState.cTensorHandles
        var inputTensors = inputStateTensorHandles.map {
            TFETensorHandle(_owning: $0)
        }
        debugLog("Getting input data tensor handles.")
        let inputDataTensorHandles =  data.cTensorHandles
        inputTensors.append(contentsOf: inputDataTensorHandles.map {
            TFETensorHandle(_owning: $0)
        })

        debugLog("Executing trace graph function.")
        let returnValues = traceContext.execute(traceeInputs: inputTensors)

        debugLog("Creating output model instance.")
        let newState = state._makeInstance(
            owning: returnValues.prefix(Int(state._tensorHandleCount)))
        let resultValues = returnValues.dropFirst(Int(state._tensorHandleCount))
        let result: Result? = resultValues.isEmpty ? nil : Result(_owning: resultValues)
        return (newState, result)
    }
}

// TODO: rename this to `graph` when it's ready for end users.
public func _graph<State: _TensorArrayProtocolEnhanced, Data: TensorGroup, Result: TensorGroup>(
    with state: State,
    in fn: (State, Data) -> (State, Result)
) -> (State, Data) -> (State, Result) {
    let graphFunction = _graphInternal(with: state, in: fn)
    return { (state: State, data: Data) in
        let result = graphFunction(state, data)
        internalConsistencyCheck(result.1 != nil)
        return (result.0, result.1!)
    }
}

// TODO: rename this to `graph` when it's ready for end users.
public func _graph<State: _TensorArrayProtocolEnhanced, Data: TensorGroup>(
    with state: State,
    in fn: (State, Data) -> State
) -> (State, Data) -> State {
    let graphFunction: (State, Data) -> (State, Tensor<Float>?) =
        withoutActuallyEscaping(fn) { escapableFn in
            let wrappedFn = {
                // The result argument needs to a type that conforms to TensorGroup.
                // We are arbitrarily picking Tensor<Float> here.
                (s: State, d: Data) -> (State, Tensor<Float>?) in
                (escapableFn(s, d), nil)
            }
            return _graphInternal(with: state, in: wrappedFn)
        }
    return { (state: State, data: Data) in graphFunction(state, data).0 }
}

/// Trace the given function `fn` and return a closure that can be used to create a
/// `TF_Function(State)` specialized for `data`.
public func _tffunc<State: _TensorArrayProtocolEnhanced, Data: TensorGroup>(
    with state: State,
    in fn: (State, Data) -> State
) -> (Data) -> (String) {
    let traceContext: TraceContext = withoutActuallyEscaping(fn) { escapableFn in
        let wrappedFn = { (inputs: [CTensorHandle]) -> [CTensorHandle] in
            let symbolicState = state._makeInstance(owning: inputs.dropLast(Data._typeList.count))
            let symbolicData = Data(_copying: inputs.dropFirst(Int(state._tensorHandleCount)))
            let outputState = escapableFn(symbolicState, symbolicData)
            return outputState.cTensorHandles
        }
        let dtypes = state._dtypes + Data._typeList.map { $0._cDataType }
        return _trace(with: dtypes, in: wrappedFn)
    }
    return {
        data in traceContext.specializeTFFunction(with: data.cTensorHandles)
    }
}


#endif
import CTensorFlow

/// The configuration for the compiler runtime.
// TODO(hongm): Revisit the longer-term design.
// @_frozen // SR-9739
public enum _RuntimeConfig {
    // TODO: change this and subsequent properties from static to thread local.
    /// When false, tensorflow runtime will be initialized before running any tensor program in this
    /// process.
    static public var tensorFlowRuntimeInitialized = false
