text: 'use strict'
  
  const constants = require('../constants')
  const lineBuilder = require('../lineBuilder')
  const recursorUtils = require('../recursorUtils')
  
  const DEEP_EQUAL = constants.DEEP_EQUAL
  const UNEQUAL = constants.UNEQUAL
  
  function describeIterableRecursor (recursor) {
    return new IterableStats(recursor.size)
  }
  exports.describeIterableRecursor = describeIterableRecursor
  
  function describeListRecursor (recursor) {
    return new ListStats(recursor.size)
  }
  exports.describeListRecursor = describeListRecursor
  
  function describePropertyRecursor (recursor) {
    return new PropertyStats(recursor.size)
  }
  exports.describePropertyRecursor = describePropertyRecursor
  
  function deserializeIterableStats (size) {
    return new IterableStats(size)
  }
  exports.deserializeIterableStats = deserializeIterableStats
  
  function deserializeListStats (size) {
    return new ListStats(size)
  }
  exports.deserializeListStats = deserializeListStats
  
  function deserializePropertyStats (size) {
    return new PropertyStats(size)
  }
  exports.deserializePropertyStats = deserializePropertyStats
  
  const iterableTag = Symbol('IterableStats')
  exports.iterableTag = iterableTag
  
  const listTag = Symbol('ListStats')
  exports.listTag = listTag
  
  const propertyTag = Symbol('PropertyStats')
  exports.propertyTag = propertyTag
  
  class Stats {
    constructor (size) {
      this.size = size
    }
  
    formatDeep (theme) {
      return lineBuilder.single(theme.stats.separator)
    }
  
    prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape) {
      if (expected.isStats !== true || expected.tag === this.tag) return null
  
      // Try to line up stats descriptors with the same tag.
      const rhsFork = recursorUtils.fork(rhsRecursor)
      const initialExpected = expected
  
      const missing = []
      while (expected !== null && this.tag !== expected.tag) {
        missing.push(expected)
        expected = rhsFork.shared()
      }
  
      if (expected !== null && missing.length > 0) {
        return {
          multipleAreMissing: true,
          descriptors: missing,
          lhsRecursor: recursorUtils.unshift(lhsRecursor, this),
          // Use original `rhsRecursor`, not `rhsFork`, since the consumed
          // descriptors are returned with the `missing` array.
          rhsRecursor: recursorUtils.unshift(rhsRecursor, expected)
        }
      }
  
      const lhsFork = recursorUtils.fork(lhsRecursor)
      let actual = this
  
      const extraneous = []
      while (actual !== null && actual.tag !== initialExpected.tag) {
        extraneous.push(actual)
        actual = lhsFork.shared()
      }
  
      if (actual !== null && extraneous.length > 0) {
        return {
          multipleAreExtraneous: true,
          descriptors: extraneous,
          // Use original `lhsRecursor`, not `lhsFork`, since the consumed
          // descriptors are returned with the `extraneous` array.
          lhsRecursor: recursorUtils.unshift(lhsRecursor, actual),
          rhsRecursor: recursorUtils.unshift(rhsFork.recursor, initialExpected)
        }
      }
  
      return null
    }
  
    serialize () {
      return this.size
    }
  }
  Object.defineProperty(Stats.prototype, 'isStats', { value: true })
  
  class IterableStats extends Stats {
    compare (expected) {
      return expected.tag === iterableTag && this.size === expected.size
        ? DEEP_EQUAL
        : UNEQUAL
    }
  }
  Object.defineProperty(IterableStats.prototype, 'tag', { value: iterableTag })
  
  class ListStats extends Stats {
    compare (expected) {
      return expected.tag === listTag && this.size === expected.size
        ? DEEP_EQUAL
        : UNEQUAL
    }
  }
  Object.defineProperty(ListStats.prototype, 'tag', { value: listTag })
  
  class PropertyStats extends Stats {
    compare (expected) {
      return expected.tag === propertyTag && this.size === expected.size
        ? DEEP_EQUAL
        : UNEQUAL
    }
  }
  Object.defineProperty(PropertyStats.prototype, 'tag', { value: propertyTag })
  