func StrategicMergePatchUsingLookupPatchMeta(original, patch []byte, schema LookupPatchMeta) ([]byte, error) {
	originalMap, err := handleUnmarshal(original)
	if err != nil {
		return nil, err
	}
	patchMap, err := handleUnmarshal(patch)
	if err != nil {
		return nil, err
	}

	result, err := StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, schema)
	if err != nil {
		return nil, err
	}

	return json.Marshal(result)
}


func handleUnmarshal(j []byte) (map[string]interface{}, error) {
	if j == nil {
		j = []byte("{}")
	}

	m := map[string]interface{}{}
	err := json.Unmarshal(j, &m)
	if err != nil {
		return nil, mergepatch.ErrBadJSONDoc
	}
	return m, nil
}


// StrategicMergeMapPatch applies a strategic merge patch. The original and patch documents
// must be JSONMap. A patch can be created from an original and modified document by
// calling CreateTwoWayMergeMapPatch.
// Warning: the original and patch JSONMap objects are mutated by this function and should not be reused.
func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JSONMap, error) {
	schema, err := NewPatchMetaFromStruct(dataStruct)
	if err != nil {
		return nil, err
	}

	// We need the go struct tags `patchMergeKey` and `patchStrategy` for fields that support a strategic merge patch.
	// For native resources, we can easily figure out these tags since we know the fields.

	// Because custom resources are decoded as Unstructured and because we're missing the metadata about how to handle
	// each field in a strategic merge patch, we can't find the go struct tags. Hence, we can't easily  do a strategic merge
	// for custom resources. So we should fail fast and return an error.
	if _, ok := dataStruct.(*unstructured.Unstructured); ok {
		return nil, mergepatch.ErrUnsupportedStrategicMergePatchFormat
	}

	return StrategicMergeMapPatchUsingLookupPatchMeta(original, patch, schema)
}


func StrategicMergeMapPatchUsingLookupPatchMeta(original, patch JSONMap, schema LookupPatchMeta) (JSONMap, error) {
	mergeOptions := MergeOptions{
		MergeParallelList:    true,
		IgnoreUnmatchedNulls: true,
	}
	return mergeMap(original, patch, schema, mergeOptions)
}


// MergeStrategicMergeMapPatchUsingLookupPatchMeta merges strategic merge
// patches retaining `null` fields and parallel lists. If 2 patches change the
// same fields and the latter one will override the former one. If you don't
// want that happen, you need to run func MergingMapsHaveConflicts before
// merging these patches. Applying the resulting merged merge patch to a JSONMap
// yields the same as merging each strategic merge patch to the JSONMap in
// succession.
func MergeStrategicMergeMapPatchUsingLookupPatchMeta(schema LookupPatchMeta, patches ...JSONMap) (JSONMap, error) {
	mergeOptions := MergeOptions{
		MergeParallelList:    false,
		IgnoreUnmatchedNulls: false,
	}
	merged := JSONMap{}
	var err error
	for _, patch := range patches {
		merged, err = mergeMap(merged, patch, schema, mergeOptions)
		if err != nil {
			return nil, err
		}
	}
	return merged, nil
}


// handleDirectiveInMergeMap handles the patch directive when merging 2 maps.
func handleDirectiveInMergeMap(directive interface{}, patch map[string]interface{}) (map[string]interface{}, error) {
	if directive == replaceDirective {
		// If the patch contains "$patch: replace", don't merge it, just use the
		// patch directly. Later on, we can add a single level replace that only
		// affects the map that the $patch is in.
		delete(patch, directiveMarker)
		return patch, nil
	}

	if directive == deleteDirective {
		// If the patch contains "$patch: delete", don't merge it, just return
		//  an empty map.
		return map[string]interface{}{}, nil
	}

	return nil, mergepatch.ErrBadPatchType(directive, patch)
}


func containsDirectiveMarker(item interface{}) bool {
	m, ok := item.(map[string]interface{})
	if ok {
		if _, foundDirectiveMarker := m[directiveMarker]; foundDirectiveMarker {
			return true
		}
	}
	return false
}


func mergeKeyValueEqual(left, right interface{}, mergeKey string) (bool, error) {
	if len(mergeKey) == 0 {
		return left == right, nil
	}
	typedLeft, ok := left.(map[string]interface{})
	if !ok {
		return false, mergepatch.ErrBadArgType(typedLeft, left)
	}
	typedRight, ok := right.(map[string]interface{})
	if !ok {
		return false, mergepatch.ErrBadArgType(typedRight, right)
	}
	mergeKeyLeft, ok := typedLeft[mergeKey]
	if !ok {
		return false, mergepatch.ErrNoMergeKey(typedLeft, mergeKey)
	}
	mergeKeyRight, ok := typedRight[mergeKey]
	if !ok {
		return false, mergepatch.ErrNoMergeKey(typedRight, mergeKey)
	}
	return mergeKeyLeft == mergeKeyRight, nil
}


// extractKey trims the prefix and return the original key
func extractKey(s, prefix string) (string, error) {
	substrings := strings.SplitN(s, "/", 2)
	if len(substrings) <= 1 || substrings[0] != prefix {
		switch prefix {
		case deleteFromPrimitiveListDirectivePrefix:
			return "", mergepatch.ErrBadPatchFormatForPrimitiveList
		case setElementOrderDirectivePrefix:
			return "", mergepatch.ErrBadPatchFormatForSetElementOrderList
		default:
			return "", fmt.Errorf("fail to find unknown prefix %q in %s\n", prefix, s)
		}
	}
	return substrings[1], nil
}


// validatePatchUsingSetOrderList verifies:
// the relative order of any two items in the setOrderList list matches that in the patch list.
// the items in the patch list must be a subset or the same as the $setElementOrder list (deletions are ignored).
func validatePatchWithSetOrderList(patchList, setOrderList interface{}, mergeKey string) error {
	typedSetOrderList, ok := setOrderList.([]interface{})
	if !ok {
		return mergepatch.ErrBadPatchFormatForSetElementOrderList
	}
	typedPatchList, ok := patchList.([]interface{})
	if !ok {
		return mergepatch.ErrBadPatchFormatForSetElementOrderList
	}
	if len(typedSetOrderList) == 0 || len(typedPatchList) == 0 {
		return nil
	}

	var nonDeleteList []interface{}
	var err error
	if len(mergeKey) > 0 {
		nonDeleteList, _, err = extractToDeleteItems(typedPatchList)
		if err != nil {
			return err
		}
	} else {
		nonDeleteList = typedPatchList
	}

	patchIndex, setOrderIndex := 0, 0
	for patchIndex < len(nonDeleteList) && setOrderIndex < len(typedSetOrderList) {
		if containsDirectiveMarker(nonDeleteList[patchIndex]) {
			patchIndex++
			continue
		}
		mergeKeyEqual, err := mergeKeyValueEqual(nonDeleteList[patchIndex], typedSetOrderList[setOrderIndex], mergeKey)
		if err != nil {
			return err
		}
		if mergeKeyEqual {
			patchIndex++
		}
		setOrderIndex++
	}
	// If patchIndex is inbound but setOrderIndex if out of bound mean there are items mismatching between the patch list and setElementOrder list.
	// the second check is a sanity check, and should always be true if the first is true.
	if patchIndex < len(nonDeleteList) && setOrderIndex >= len(typedSetOrderList) {
		return fmt.Errorf("The order in patch list:\n%v\n doesn't match %s list:\n%v\n", typedPatchList, setElementOrderDirectivePrefix, setOrderList)
	}
	return nil
}


// preprocessDeletionListForMerging preprocesses the deletion list.
// it returns shouldContinue, isDeletionList, noPrefixKey
func preprocessDeletionListForMerging(key string, original map[string]interface{},
	patchVal interface{}, mergeDeletionList bool) (bool, bool, string, error) {
	// If found a parallel list for deletion and we are going to merge the list,
	// overwrite the key to the original key and set flag isDeleteList
	foundParallelListPrefix := strings.HasPrefix(key, deleteFromPrimitiveListDirectivePrefix)
	if foundParallelListPrefix {
		if !mergeDeletionList {
			original[key] = patchVal
			return true, false, "", nil
		}
		originalKey, err := extractKey(key, deleteFromPrimitiveListDirectivePrefix)
		return false, true, originalKey, err
	}
	return false, false, "", nil
}


// applyRetainKeysDirective looks for a retainKeys directive and applies to original
// - if no directive exists do nothing
// - if directive is found, clear keys in original missing from the directive list
// - validate that all keys present in the patch are present in the retainKeys directive
// note: original may be another patch request, e.g. applying the add+modified patch to the deletions patch. In this case it may have directives
func applyRetainKeysDirective(original, patch map[string]interface{}, options MergeOptions) error {
	retainKeysInPatch, foundInPatch := patch[retainKeysDirective]
	if !foundInPatch {
		return nil
	}
	// cleanup the directive
	delete(patch, retainKeysDirective)

	if !options.MergeParallelList {
		// If original is actually a patch, make sure the retainKeys directives are the same in both patches if present in both.
		// If not present in the original patch, copy from the modified patch.
		retainKeysInOriginal, foundInOriginal := original[retainKeysDirective]
		if foundInOriginal {
			if !reflect.DeepEqual(retainKeysInOriginal, retainKeysInPatch) {
				// This error actually should never happen.
				return fmt.Errorf("%v and %v are not deep equal: this may happen when calculating the 3-way diff patch", retainKeysInOriginal, retainKeysInPatch)
			}
		} else {
			original[retainKeysDirective] = retainKeysInPatch
		}
		return nil
	}

	retainKeysList, ok := retainKeysInPatch.([]interface{})
	if !ok {
		return mergepatch.ErrBadPatchFormatForRetainKeys
	}

	// validate patch to make sure all fields in the patch are present in the retainKeysList.
	// The map is used only as a set, the value is never referenced
	m := map[interface{}]struct{}{}
	for _, v := range retainKeysList {
		m[v] = struct{}{}
	}
	for k, v := range patch {
		if v == nil || strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) ||
			strings.HasPrefix(k, setElementOrderDirectivePrefix) {
			continue
		}
		// If there is an item present in the patch but not in the retainKeys list,
		// the patch is invalid.
		if _, found := m[k]; !found {
			return mergepatch.ErrBadPatchFormatForRetainKeys
		}
	}

	// clear not present fields
	for k := range original {
		if _, found := m[k]; !found {
			delete(original, k)
		}
	}
	return nil
}


// mergePatchIntoOriginal processes $setElementOrder list.
// When not merging the directive, it will make sure $setElementOrder list exist only in original.
// When merging the directive, it will try to find the $setElementOrder list and
// its corresponding patch list, validate it and merge it.
// Then, sort them by the relative order in setElementOrder, patch list and live list.
// The precedence is $setElementOrder > order in patch list > order in live list.
// This function will delete the item after merging it to prevent process it again in the future.
// Ref: https://git.k8s.io/design-proposals-archive/cli/preserve-order-in-strategic-merge-patch.md
func mergePatchIntoOriginal(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) error {
	for key, patchV := range patch {
		// Do nothing if there is no ordering directive
		if !strings.HasPrefix(key, setElementOrderDirectivePrefix) {
			continue
		}

		setElementOrderInPatch := patchV
		// Copies directive from the second patch (`patch`) to the first patch (`original`)
		// and checks they are equal and delete the directive in the second patch
		if !mergeOptions.MergeParallelList {
			setElementOrderListInOriginal, ok := original[key]
			if ok {
				// check if the setElementOrder list in original and the one in patch matches
				if !reflect.DeepEqual(setElementOrderListInOriginal, setElementOrderInPatch) {
					return mergepatch.ErrBadPatchFormatForSetElementOrderList
				}
			} else {
				// move the setElementOrder list from patch to original
				original[key] = setElementOrderInPatch
			}
		}
		delete(patch, key)

		var (
			ok                                          bool
			originalFieldValue, patchFieldValue, merged []interface{}
			patchStrategy                               string
			patchMeta                                   PatchMeta
			subschema                                   LookupPatchMeta
		)
		typedSetElementOrderList, ok := setElementOrderInPatch.([]interface{})
		if !ok {
			return mergepatch.ErrBadArgType(typedSetElementOrderList, setElementOrderInPatch)
		}
		// Trim the setElementOrderDirectivePrefix to get the key of the list field in original.
		originalKey, err := extractKey(key, setElementOrderDirectivePrefix)
		if err != nil {
			return err
		}
		// try to find the list with `originalKey` in `original` and `modified` and merge them.
		originalList, foundOriginal := original[originalKey]
		patchList, foundPatch := patch[originalKey]
		if foundOriginal {
			originalFieldValue, ok = originalList.([]interface{})
			if !ok {
				return mergepatch.ErrBadArgType(originalFieldValue, originalList)
			}
		}
		if foundPatch {
			patchFieldValue, ok = patchList.([]interface{})
			if !ok {
				return mergepatch.ErrBadArgType(patchFieldValue, patchList)
			}
		}
		subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(originalKey)
		if err != nil {
			return err
		}
		_, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
		if err != nil {
			return err
		}
		// Check for consistency between the element order list and the field it applies to
		err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
		if err != nil {
			return err
		}

		switch {
		case foundOriginal && !foundPatch:
			// no change to list contents
			merged = originalFieldValue
		case !foundOriginal && foundPatch:
			// list was added
			merged = patchFieldValue
		case foundOriginal && foundPatch:
			merged, err = mergeSliceHandler(originalList, patchList, subschema,
				patchStrategy, patchMeta.GetPatchMergeKey(), false, mergeOptions)
			if err != nil {
				return err
			}
		case !foundOriginal && !foundPatch:
			continue
		}

		// Split all items into patch items and server-only items and then enforce the order.
		var patchItems, serverOnlyItems []interface{}
		if len(patchMeta.GetPatchMergeKey()) == 0 {
			// Primitives doesn't need merge key to do partitioning.
			patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, typedSetElementOrderList)

		} else {
			// Maps need merge key to do partitioning.
			patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
			if err != nil {
				return err
			}
		}

		elementType, err := sliceElementType(originalFieldValue, patchFieldValue)
		if err != nil {
			return err
		}
		kind := elementType.Kind()
		// normalize merged list
		// typedSetElementOrderList contains all the relative order in typedPatchList,
		// so don't need to use typedPatchList
		both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, patchMeta.GetPatchMergeKey(), kind)
		if err != nil {
			return err
		}
		original[originalKey] = both
		// delete patch list from patch to prevent process again in the future
		delete(patch, originalKey)
	}
	return nil
}


// partitionPrimitivesByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not.
func partitionPrimitivesByPresentInList(original, partitionBy []interface{}) ([]interface{}, []interface{}) {
	patch := make([]interface{}, 0, len(original))
	serverOnly := make([]interface{}, 0, len(original))
	inPatch := map[interface{}]bool{}
	for _, v := range partitionBy {
		inPatch[v] = true
	}
	for _, v := range original {
		if !inPatch[v] {
			serverOnly = append(serverOnly, v)
		} else {
			patch = append(patch, v)
		}
	}
	return patch, serverOnly
}


// partitionMapsByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not.
func partitionMapsByPresentInList(original, partitionBy []interface{}, mergeKey string) ([]interface{}, []interface{}, error) {
	patch := make([]interface{}, 0, len(original))
	serverOnly := make([]interface{}, 0, len(original))
	for _, v := range original {
		typedV, ok := v.(map[string]interface{})
		if !ok {
			return nil, nil, mergepatch.ErrBadArgType(typedV, v)
		}
		mergeKeyValue, foundMergeKey := typedV[mergeKey]
		if !foundMergeKey {
			return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey)
		}
		_, _, found, err := findMapInSliceBasedOnKeyValue(partitionBy, mergeKey, mergeKeyValue)
		if err != nil {
			return nil, nil, err
		}
		if !found {
			serverOnly = append(serverOnly, v)
		} else {
			patch = append(patch, v)
		}
	}
	return patch, serverOnly, nil
}


// Merge fields from a patch map into the original map. Note: This may modify
// both the original map and the patch because getting a deep copy of a map in
// golang is highly non-trivial.
// flag mergeOptions.MergeParallelList controls if using the parallel list to delete or keeping the list.
// If patch contains any null field (e.g. field_1: null) that is not
// present in original, then to propagate it to the end result use
// mergeOptions.IgnoreUnmatchedNulls == false.
func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) (map[string]interface{}, error) {
	if v, ok := patch[directiveMarker]; ok {
		return handleDirectiveInMergeMap(v, patch)
	}

	// nil is an accepted value for original to simplify logic in other places.
	// If original is nil, replace it with an empty map and then apply the patch.
	if original == nil {
		original = map[string]interface{}{}
	}

	err := applyRetainKeysDirective(original, patch, mergeOptions)
	if err != nil {
		return nil, err
	}

	// Process $setElementOrder list and other lists sharing the same key.
	// When not merging the directive, it will make sure $setElementOrder list exist only in original.
	// When merging the directive, it will process $setElementOrder and its patch list together.
	// This function will delete the merged elements from patch so they will not be reprocessed
	err = mergePatchIntoOriginal(original, patch, schema, mergeOptions)
	if err != nil {
		return nil, err
	}

	// Start merging the patch into the original.
	for k, patchV := range patch {
		skipProcessing, isDeleteList, noPrefixKey, err := preprocessDeletionListForMerging(k, original, patchV, mergeOptions.MergeParallelList)
		if err != nil {
			return nil, err
		}
		if skipProcessing {
			continue
		}
		if len(noPrefixKey) > 0 {
			k = noPrefixKey
		}

		// If the value of this key is null, delete the key if it exists in the
		// original. Otherwise, check if we want to preserve it or skip it.
		// Preserving the null value is useful when we want to send an explicit
		// delete to the API server.
		if patchV == nil {
			delete(original, k)
			if mergeOptions.IgnoreUnmatchedNulls {
				continue
			}
		}

		_, ok := original[k]
		if !ok {
			if !isDeleteList {
				// If it's not in the original document, just take the patch value.
				if mergeOptions.IgnoreUnmatchedNulls {
					discardNullValuesFromPatch(patchV)
				}
				original[k] = patchV
			}
			continue
		}

		originalType := reflect.TypeOf(original[k])
		patchType := reflect.TypeOf(patchV)
		if originalType != patchType {
			if !isDeleteList {
				if mergeOptions.IgnoreUnmatchedNulls {
					discardNullValuesFromPatch(patchV)
				}
				original[k] = patchV
			}
			continue
		}
		// If they're both maps or lists, recurse into the value.
		switch originalType.Kind() {
		case reflect.Map:
			subschema, patchMeta, err2 := schema.LookupPatchMetadataForStruct(k)
			if err2 != nil {
				return nil, err2
			}
			_, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
			if err2 != nil {
				return nil, err2
			}
			original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions)
		case reflect.Slice:
			subschema, patchMeta, err2 := schema.LookupPatchMetadataForSlice(k)
			if err2 != nil {
				return nil, err2
			}
			_, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
			if err2 != nil {
				return nil, err2
			}
			original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions)
		default:
			original[k] = patchV
		}
		if err != nil {
			return nil, err
		}
	}
	return original, nil
}


// discardNullValuesFromPatch discards all null property values from patch.
// It traverses all slices and map types.
func discardNullValuesFromPatch(patchV interface{}) {
	switch patchV := patchV.(type) {
	case map[string]interface{}:
		for k, v := range patchV {
			if v == nil {
				delete(patchV, k)
			} else {
				discardNullValuesFromPatch(v)
			}
		}
	case []interface{}:
		for _, v := range patchV {
			discardNullValuesFromPatch(v)
		}
	}
}


// mergeMapHandler handles how to merge `patchV` whose key is `key` with `original` respecting
// fieldPatchStrategy and mergeOptions.
func mergeMapHandler(original, patch interface{}, schema LookupPatchMeta,
	fieldPatchStrategy string, mergeOptions MergeOptions) (map[string]interface{}, error) {
	typedOriginal, typedPatch, err := mapTypeAssertion(original, patch)
	if err != nil {
		return nil, err
	}

	if fieldPatchStrategy != replaceDirective {
		return mergeMap(typedOriginal, typedPatch, schema, mergeOptions)
	} else {
		return typedPatch, nil
	}
}


// mergeSliceHandler handles how to merge `patchV` whose key is `key` with `original` respecting
// fieldPatchStrategy, fieldPatchMergeKey, isDeleteList and mergeOptions.
func mergeSliceHandler(original, patch interface{}, schema LookupPatchMeta,
	fieldPatchStrategy, fieldPatchMergeKey string, isDeleteList bool, mergeOptions MergeOptions) ([]interface{}, error) {
	typedOriginal, typedPatch, err := sliceTypeAssertion(original, patch)
	if err != nil {
		return nil, err
	}

	if fieldPatchStrategy == mergeDirective {
		return mergeSlice(typedOriginal, typedPatch, schema, fieldPatchMergeKey, mergeOptions, isDeleteList)
	} else {
		return typedPatch, nil
	}
}


// Merge two slices together. Note: This may modify both the original slice and
// the patch because getting a deep copy of a slice in golang is highly
// non-trivial.
func mergeSlice(original, patch []interface{}, schema LookupPatchMeta, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) {
	if len(original) == 0 && len(patch) == 0 {
		return original, nil
	}

	// All the values must be of the same type, but not a list.
	t, err := sliceElementType(original, patch)
	if err != nil {
		return nil, err
	}

	var merged []interface{}
	kind := t.Kind()
	// If the elements are not maps, merge the slices of scalars.
	if kind != reflect.Map {
		if mergeOptions.MergeParallelList && isDeleteList {
			return deleteFromSlice(original, patch), nil
		}
		// Maybe in the future add a "concat" mode that doesn't
		// deduplicate.
		both := append(original, patch...)
		merged = deduplicateScalars(both)

	} else {
		if mergeKey == "" {
			return nil, fmt.Errorf("cannot merge lists without merge key for %s", schema.Name())
		}

		original, patch, err = mergeSliceWithSpecialElements(original, patch, mergeKey)
		if err != nil {
			return nil, err
		}

		merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, schema, mergeOptions)
		if err != nil {
			return nil, err
		}
	}

	// enforce the order
	var patchItems, serverOnlyItems []interface{}
	if len(mergeKey) == 0 {
		patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, patch)
	} else {
		patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, patch, mergeKey)
		if err != nil {
			return nil, err
		}
	}
	return normalizeElementOrder(patchItems, serverOnlyItems, patch, original, mergeKey, kind)
}


// mergeSliceWithSpecialElements handles special elements with directiveMarker
// before merging the slices. It returns a updated `original` and a patch without special elements.
// original and patch must be slices of maps, they should be checked before calling this function.
func mergeSliceWithSpecialElements(original, patch []interface{}, mergeKey string) ([]interface{}, []interface{}, error) {
	patchWithoutSpecialElements := []interface{}{}
	replace := false
	for _, v := range patch {
		typedV := v.(map[string]interface{})
		patchType, ok := typedV[directiveMarker]
		if !ok {
			patchWithoutSpecialElements = append(patchWithoutSpecialElements, v)
		} else {
			switch patchType {
			case deleteDirective:
				mergeValue, ok := typedV[mergeKey]
				if ok {
					var err error
					original, err = deleteMatchingEntries(original, mergeKey, mergeValue)
					if err != nil {
						return nil, nil, err
					}
				} else {
					return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey)
				}
			case replaceDirective:
				replace = true
				// Continue iterating through the array to prune any other $patch elements.
			case mergeDirective:
				return nil, nil, fmt.Errorf("merging lists cannot yet be specified in the patch")
			default:
				return nil, nil, mergepatch.ErrBadPatchType(patchType, typedV)
			}
		}
	}
	if replace {
		return patchWithoutSpecialElements, nil, nil
	}
	return original, patchWithoutSpecialElements, nil
}


// delete all matching entries (based on merge key) from a merging list
func deleteMatchingEntries(original []interface{}, mergeKey string, mergeValue interface{}) ([]interface{}, error) {
	for {
		_, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue)
		if err != nil {
			return nil, err
		}

		if !found {
			break
		}
		// Delete the element at originalKey.
		original = append(original[:originalKey], original[originalKey+1:]...)
	}
	return original, nil
}


// mergeSliceWithoutSpecialElements merges slices with non-special elements.
// original and patch must be slices of maps, they should be checked before calling this function.
func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, schema LookupPatchMeta, mergeOptions MergeOptions) ([]interface{}, error) {
	for _, v := range patch {
		typedV := v.(map[string]interface{})
		mergeValue, ok := typedV[mergeKey]
		if !ok {
			return nil, mergepatch.ErrNoMergeKey(typedV, mergeKey)
		}

		// If we find a value with this merge key value in original, merge the
		// maps. Otherwise append onto original.
		originalMap, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue)
		if err != nil {
			return nil, err
		}

		if found {
			var mergedMaps interface{}
			var err error
			// Merge into original.
			mergedMaps, err = mergeMap(originalMap, typedV, schema, mergeOptions)
			if err != nil {
				return nil, err
			}

			original[originalKey] = mergedMaps
		} else {
			original = append(original, v)
		}
	}
	return original, nil
}


// deleteFromSlice uses the parallel list to delete the items in a list of scalars
func deleteFromSlice(current, toDelete []interface{}) []interface{} {
	toDeleteMap := map[interface{}]interface{}{}
	processed := make([]interface{}, 0, len(current))
	for _, v := range toDelete {
		toDeleteMap[v] = true
	}
	for _, v := range current {
		if _, found := toDeleteMap[v]; !found {
			processed = append(processed, v)
		}
	}
	return processed
}


// This method no longer panics if any element of the slice is not a map.
func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{}) (map[string]interface{}, int, bool, error) {
	for k, v := range m {
		typedV, ok := v.(map[string]interface{})
		if !ok {
			return nil, 0, false, fmt.Errorf("value for key %v is not a map", k)
		}

		valueToMatch, ok := typedV[key]
		if ok && valueToMatch == value {
			return typedV, k, true, nil
		}
	}

	return nil, 0, false, nil
}


// This function takes a JSON map and sorts all the lists that should be merged
// by key. This is needed by tests because in JSON, list order is significant,
// but in Strategic Merge Patch, merge lists do not have significant order.
// Sorting the lists allows for order-insensitive comparison of patched maps.
func sortMergeListsByName(mapJSON []byte, schema LookupPatchMeta) ([]byte, error) {
	var m map[string]interface{}
	err := json.Unmarshal(mapJSON, &m)
	if err != nil {
		return nil, mergepatch.ErrBadJSONDoc
	}

	newM, err := sortMergeListsByNameMap(m, schema)
	if err != nil {
		return nil, err
	}

	return json.Marshal(newM)
}


// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map.
func sortMergeListsByNameMap(s map[string]interface{}, schema LookupPatchMeta) (map[string]interface{}, error) {
	newS := map[string]interface{}{}
	for k, v := range s {
		if k == retainKeysDirective {
			typedV, ok := v.([]interface{})
			if !ok {
				return nil, mergepatch.ErrBadPatchFormatForRetainKeys
			}
			v = sortScalars(typedV)
		} else if strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) {
			typedV, ok := v.([]interface{})
			if !ok {
				return nil, mergepatch.ErrBadPatchFormatForPrimitiveList
			}
			v = sortScalars(typedV)
		} else if strings.HasPrefix(k, setElementOrderDirectivePrefix) {
			_, ok := v.([]interface{})
			if !ok {
				return nil, mergepatch.ErrBadPatchFormatForSetElementOrderList
			}
		} else if k != directiveMarker {
			// recurse for map and slice.
			switch typedV := v.(type) {
			case map[string]interface{}:
				subschema, _, err := schema.LookupPatchMetadataForStruct(k)
				if err != nil {
					return nil, err
				}
				v, err = sortMergeListsByNameMap(typedV, subschema)
				if err != nil {
					return nil, err
				}
			case []interface{}:
				subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k)
				if err != nil {
					return nil, err
				}
				_, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
				if err != nil {
					return nil, err
				}
				if patchStrategy == mergeDirective {
					var err error
					v, err = sortMergeListsByNameArray(typedV, subschema, patchMeta.GetPatchMergeKey(), true)
					if err != nil {
						return nil, err
					}
				}
			}
		}

		newS[k] = v
	}

	return newS, nil
}


// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in an array.
func sortMergeListsByNameArray(s []interface{}, schema LookupPatchMeta, mergeKey string, recurse bool) ([]interface{}, error) {
	if len(s) == 0 {
		return s, nil
	}

	// We don't support lists of lists yet.
	t, err := sliceElementType(s)
	if err != nil {
		return nil, err
	}

	// If the elements are not maps...
	if t.Kind() != reflect.Map {
		// Sort the elements, because they may have been merged out of order.
		return deduplicateAndSortScalars(s), nil
	}

	// Elements are maps - if one of the keys of the map is a map or a
	// list, we may need to recurse into it.
	newS := []interface{}{}
	for _, elem := range s {
		if recurse {
			typedElem := elem.(map[string]interface{})
			newElem, err := sortMergeListsByNameMap(typedElem, schema)
			if err != nil {
				return nil, err
			}

			newS = append(newS, newElem)
		} else {
			newS = append(newS, elem)
		}
	}

	// Sort the maps.
	newS = sortMapsBasedOnField(newS, mergeKey)
	return newS, nil
}


func sortMapsBasedOnField(m []interface{}, fieldName string) []interface{} {
	mapM := mapSliceFromSlice(m)
	ss := SortableSliceOfMaps{mapM, fieldName}
	sort.Sort(ss)
	newS := sliceFromMapSlice(ss.s)
	return newS
}


func mapSliceFromSlice(m []interface{}) []map[string]interface{} {
	newM := []map[string]interface{}{}
	for _, v := range m {
		vt := v.(map[string]interface{})
		newM = append(newM, vt)
	}

	return newM
}


func sliceFromMapSlice(s []map[string]interface{}) []interface{} {
	newS := []interface{}{}
	for _, v := range s {
		newS = append(newS, v)
	}

	return newS
}


func (ss SortableSliceOfMaps) Len() int

func (ss SortableSliceOfMaps) Len() int {
	return len(ss.s)
}


func (ss SortableSliceOfMaps) Less(i, j int) bool {
	iStr := fmt.Sprintf("%v", ss.s[i][ss.k])
	jStr := fmt.Sprintf("%v", ss.s[j][ss.k])
	return sort.StringsAreSorted([]string{iStr, jStr})
}


func (ss SortableSliceOfMaps) Swap(i, j int) {
	tmp := ss.s[i]
	ss.s[i] = ss.s[j]
	ss.s[j] = tmp
}


func deduplicateAndSortScalars(s []interface{}) []interface{} {
	s = deduplicateScalars(s)
	return sortScalars(s)
}


func sortScalars(s []interface{}) []interface{} {
	ss := SortableSliceOfScalars{s}
	sort.Sort(ss)
	return ss.s
}


func deduplicateScalars(s []interface{}) []interface{} {
	// Clever algorithm to deduplicate.
	length := len(s) - 1
	for i := 0; i < length; i++ {
		for j := i + 1; j <= length; j++ {
			if s[i] == s[j] {
				s[j] = s[length]
				s = s[0:length]
				length--
				j--
			}
		}
	}

	return s
}


func (ss SortableSliceOfScalars) Len() int {
	return len(ss.s)
}


func (ss SortableSliceOfScalars) Less(i, j int) bool {
	iStr := fmt.Sprintf("%v", ss.s[i])
	jStr := fmt.Sprintf("%v", ss.s[j])
	return sort.StringsAreSorted([]string{iStr, jStr})
}


func (ss SortableSliceOfScalars) Swap(i, j int) {
	tmp := ss.s[i]
	ss.s[i] = ss.s[j]
	ss.s[j] = tmp
}


// Returns the type of the elements of N slice(s). If the type is different,
// another slice or undefined, returns an error.
func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
	var prevType reflect.Type
	for _, s := range slices {
		// Go through elements of all given slices and make sure they are all the same type.
		for _, v := range s {
			currentType := reflect.TypeOf(v)
			if prevType == nil {
				prevType = currentType
				// We don't support lists of lists yet.
				if prevType.Kind() == reflect.Slice {
					return nil, mergepatch.ErrNoListOfLists
				}
			} else {
				if prevType != currentType {
					return nil, fmt.Errorf("list element types are not identical: %v", fmt.Sprint(slices))
				}
				prevType = currentType
			}
		}
	}

	if prevType == nil {
		return nil, fmt.Errorf("no elements in any of the given slices")
	}

	return prevType, nil
}


// MergingMapsHaveConflicts returns true if the left and right JSON interface
// objects overlap with different values in any key. All keys are required to be
// strings. Since patches of the same Type have congruent keys, this is valid
// for multiple patch types. This method supports strategic merge patch semantics.
func MergingMapsHaveConflicts(left, right map[string]interface{}, schema LookupPatchMeta) (bool, error) {
	return mergingMapFieldsHaveConflicts(left, right, schema, "", "")
}


func mergingMapFieldsHaveConflicts(

func mergingMapFieldsHaveConflicts(
	left, right interface{},
	schema LookupPatchMeta,
	fieldPatchStrategy, fieldPatchMergeKey string,
) (bool, error) {
	switch leftType := left.(type) {
	case map[string]interface{}:
		rightType, ok := right.(map[string]interface{})
		if !ok {
			return true, nil
		}
		leftMarker, okLeft := leftType[directiveMarker]
		rightMarker, okRight := rightType[directiveMarker]
		// if one or the other has a directive marker,
		// then we need to consider that before looking at the individual keys,
		// since a directive operates on the whole map.
		if okLeft || okRight {
			// if one has a directive marker and the other doesn't,
			// then we have a conflict, since one is deleting or replacing the whole map,
			// and the other is doing things to individual keys.
			if okLeft != okRight {
				return true, nil
			}
			// if they both have markers, but they are not the same directive,
			// then we have a conflict because they're doing different things to the map.
			if leftMarker != rightMarker {
				return true, nil
			}
		}
		if fieldPatchStrategy == replaceDirective {
			return false, nil
		}
		// Check the individual keys.
		return mapsHaveConflicts(leftType, rightType, schema)

	case []interface{}:
		rightType, ok := right.([]interface{})
		if !ok {
			return true, nil
		}
		return slicesHaveConflicts(leftType, rightType, schema, fieldPatchStrategy, fieldPatchMergeKey)
	case string, float64, bool, int64, nil:
		return !reflect.DeepEqual(left, right), nil
	default:
		return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
	}
}


func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
	for key, leftValue := range typedLeft {
		if key != directiveMarker && key != retainKeysDirective {
			if rightValue, ok := typedRight[key]; ok {
				var subschema LookupPatchMeta
				var patchMeta PatchMeta
				var patchStrategy string
				var err error
				switch leftValue.(type) {
				case []interface{}:
					subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(key)
					if err != nil {
						return true, err
					}
					_, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
					if err != nil {
						return true, err
					}
				case map[string]interface{}:
					subschema, patchMeta, err = schema.LookupPatchMetadataForStruct(key)
					if err != nil {
						return true, err
					}
					_, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
					if err != nil {
						return true, err
					}
				}

				if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
					subschema, patchStrategy, patchMeta.GetPatchMergeKey()); hasConflicts {
					return true, err
				}
			}
		}
	}

	return false, nil
}


func slicesHaveConflicts(

func slicesHaveConflicts(
	typedLeft, typedRight []interface{},
	schema LookupPatchMeta,
	fieldPatchStrategy, fieldPatchMergeKey string,
) (bool, error) {
	elementType, err := sliceElementType(typedLeft, typedRight)
	if err != nil {
		return true, err
	}

	if fieldPatchStrategy == mergeDirective {
		// Merging lists of scalars have no conflicts by definition
		// So we only need to check further if the elements are maps
		if elementType.Kind() != reflect.Map {
			return false, nil
		}

		// Build a map for each slice and then compare the two maps
		leftMap, err := sliceOfMapsToMapOfMaps(typedLeft, fieldPatchMergeKey)
		if err != nil {
			return true, err
		}

		rightMap, err := sliceOfMapsToMapOfMaps(typedRight, fieldPatchMergeKey)
		if err != nil {
			return true, err
		}

		return mapsOfMapsHaveConflicts(leftMap, rightMap, schema)
	}

	// Either we don't have type information, or these are non-merging lists
	if len(typedLeft) != len(typedRight) {
		return true, nil
	}

	// Sort scalar slices to prevent ordering issues
	// We have no way to sort non-merging lists of maps
	if elementType.Kind() != reflect.Map {
		typedLeft = deduplicateAndSortScalars(typedLeft)
		typedRight = deduplicateAndSortScalars(typedRight)
	}

	// Compare the slices element by element in order
	// This test will fail if the slices are not sorted
	for i := range typedLeft {
		if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], schema, "", ""); hasConflicts {
			return true, err
		}
	}

	return false, nil
}


func sliceOfMapsToMapOfMaps(slice []interface{}, mergeKey string) (map[string]interface{}, error) {
	result := make(map[string]interface{}, len(slice))
	for _, value := range slice {
		typedValue, ok := value.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("invalid element type in merging list:%v", slice)
		}

		mergeValue, ok := typedValue[mergeKey]
		if !ok {
			return nil, fmt.Errorf("cannot find merge key `%s` in merging list element:%v", mergeKey, typedValue)
		}

		result[fmt.Sprintf("%s", mergeValue)] = typedValue
	}

	return result, nil
}


func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
	for key, leftValue := range typedLeft {
		if rightValue, ok := typedRight[key]; ok {
			if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, schema, "", ""); hasConflicts {
				return true, err
			}
		}
	}

	return false, nil
}


// CreateThreeWayMergePatch reconciles a modified configuration with an original configuration,
// while preserving any changes or deletions made to the original configuration in the interim,
// and not overridden by the current configuration. All three documents must be passed to the
// method as json encoded content. It will return a strategic merge patch, or an error if any
// of the documents is invalid, or if there are any preconditions that fail against the modified
// configuration, or, if overwrite is false and there are conflicts between the modified and current
// configurations. Conflicts are defined as keys changed differently from original to modified
// than from original to current. In other words, a conflict occurs if modified changes any key
// in a way that is different from how it is changed in current (e.g., deleting it, changing its
// value). We also propagate values fields that do not exist in original but are explicitly
// defined in modified.
func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
	originalMap := map[string]interface{}{}
	if len(original) > 0 {
		if err := json.Unmarshal(original, &originalMap); err != nil {
			return nil, mergepatch.ErrBadJSONDoc
		}
	}

	modifiedMap := map[string]interface{}{}
	if len(modified) > 0 {
		if err := json.Unmarshal(modified, &modifiedMap); err != nil {
			return nil, mergepatch.ErrBadJSONDoc
		}
	}

	currentMap := map[string]interface{}{}
	if len(current) > 0 {
		if err := json.Unmarshal(current, &currentMap); err != nil {
			return nil, mergepatch.ErrBadJSONDoc
		}
	}

	// The patch is the difference from current to modified without deletions, plus deletions
	// from original to modified. To find it, we compute deletions, which are the deletions from
	// original to modified, and delta, which is the difference from current to modified without
	// deletions, and then apply delta to deletions as a patch, which should be strictly additive.
	deltaMapDiffOptions := DiffOptions{
		IgnoreDeletions: true,
		SetElementOrder: true,
	}
	deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions)
	if err != nil {
		return nil, err
	}
	deletionsMapDiffOptions := DiffOptions{
		SetElementOrder:           true,
		IgnoreChangesAndAdditions: true,
	}
	deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions)
	if err != nil {
		return nil, err
	}

	mergeOptions := MergeOptions{}
	patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions)
	if err != nil {
		return nil, err
	}

	// Apply the preconditions to the patch, and return an error if any of them fail.
	for _, fn := range fns {
		if !fn(patchMap) {
			return nil, mergepatch.NewErrPreconditionFailed(patchMap)
		}
	}

	// If overwrite is false, and the patch contains any keys that were changed differently,
	// then return a conflict error.
	if !overwrite {
		changeMapDiffOptions := DiffOptions{}
		changedMap, err := diffMaps(originalMap, currentMap, schema, changeMapDiffOptions)
		if err != nil {
			return nil, err
		}

		hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, schema)
		if err != nil {
			return nil, err
		}

		if hasConflicts {
			return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(patchMap), mergepatch.ToYAMLOrError(changedMap))
		}
	}

	return json.Marshal(patchMap)
}


func ItemAddedToModifiedSlice(original, modified string) bool { return original > modified }


func ItemRemovedFromModifiedSlice(original, modified string) bool { return original < modified }


func ItemMatchesOriginalAndModifiedSlice(original, modified string) bool { return original == modified }


func CreateDeleteDirective(mergeKey string, mergeKeyValue interface{}) map[string]interface{} {
	return map[string]interface{}{mergeKey: mergeKeyValue, directiveMarker: deleteDirective}
}


func mapTypeAssertion(original, patch interface{}) (map[string]interface{}, map[string]interface{}, error) {
	typedOriginal, ok := original.(map[string]interface{})
	if !ok {
		return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original)
	}
	typedPatch, ok := patch.(map[string]interface{})
	if !ok {
		return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch)
	}
	return typedOriginal, typedPatch, nil
}


func sliceTypeAssertion(original, patch interface{}) ([]interface{}, []interface{}, error) {
	typedOriginal, ok := original.([]interface{})
	if !ok {
		return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original)
	}
	typedPatch, ok := patch.([]interface{})
	if !ok {
		return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch)
	}
	return typedOriginal, typedPatch, nil
}


// extractRetainKeysPatchStrategy process patch strategy, which is a string may contains multiple
// patch strategies separated by ",". It returns a boolean var indicating if it has
// retainKeys strategies and a string for the other strategy.
func extractRetainKeysPatchStrategy(strategies []string) (bool, string, error) {
	switch len(strategies) {
	case 0:
		return false, "", nil
	case 1:
		singleStrategy := strategies[0]
		switch singleStrategy {
		case retainKeysStrategy:
			return true, "", nil
		default:
			return false, singleStrategy, nil
		}
	case 2:
		switch {
		case strategies[0] == retainKeysStrategy:
			return true, strategies[1], nil
		case strategies[1] == retainKeysStrategy:
			return true, strategies[0], nil
		default:
			return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies)
		}
	default:
		return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies)
	}
}


// hasAdditionalNewField returns if original map has additional key with non-nil value than modified.
func hasAdditionalNewField(original, modified map[string]interface{}) bool {
	for k, v := range original {
		if v == nil {
			continue
		}
		if _, found := modified[k]; !found {
			return true
		}
	}
	return false
}


func NewKindItem(key string, path *openapi.Path) *kindItem {
	return &kindItem{
		key:  key,
		path: path,
	}
}


func (item *kindItem) Error() error

func (item *kindItem) Error() error {
	return item.err
}


func (item *kindItem) Path() *openapi.Path {
	return item.path
}


func (item *kindItem) VisitPrimitive(schema *openapi.Primitive) {
	item.err = errors.New("expected kind, but got primitive")
}


func (item *kindItem) VisitArray(schema *openapi.Array) {
	item.err = errors.New("expected kind, but got slice")
}


func (item *kindItem) VisitMap(schema *openapi.Map) {
	item.err = errors.New("expected kind, but got map")
}


func (item *kindItem) VisitReference(schema openapi.Reference) {
	if !item.hasVisitKind {
		schema.SubSchema().Accept(item)
	}
}


func (item *kindItem) VisitKind(schema *openapi.Kind) {
	subschema, ok := schema.Fields[item.key]
	if !ok {
		item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
		return
	}

	mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
	if err != nil {
		item.err = err
		return
	}
	item.patchmeta = PatchMeta{
		patchStrategies: patchStrategies,
		patchMergeKey:   mergeKey,
	}
	item.subschema = subschema
}


func NewSliceItem(key string, path *openapi.Path) *sliceItem {
	return &sliceItem{
		key:  key,
		path: path,
	}
}


func (item *sliceItem) Error() error

func (item *sliceItem) Error() error {
	return item.err
}


func (item *sliceItem) Path() *openapi.Path {
	return item.path
}


func (item *sliceItem) VisitPrimitive(schema *openapi.Primitive) {
	item.err = errors.New("expected slice, but got primitive")
}


func (item *sliceItem) VisitArray(schema *openapi.Array) {
	if !item.hasVisitKind {
		item.err = errors.New("expected visit kind first, then visit array")
	}
	subschema := schema.SubType
	item.subschema = subschema
}


func (item *sliceItem) VisitMap(schema *openapi.Map) {
	item.err = errors.New("expected slice, but got map")
}


func (item *sliceItem) VisitReference(schema openapi.Reference) {
	if !item.hasVisitKind {
		schema.SubSchema().Accept(item)
	} else {
		item.subschema = schema.SubSchema()
	}
}


func (item *sliceItem) VisitKind(schema *openapi.Kind) {
	subschema, ok := schema.Fields[item.key]
	if !ok {
		item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
		return
	}

	mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
	if err != nil {
		item.err = err
		return
	}
	item.patchmeta = PatchMeta{
		patchStrategies: patchStrategies,
		patchMergeKey:   mergeKey,
	}
	item.hasVisitKind = true
	subschema.Accept(item)
}


func parsePatchMetadata(extensions map[string]interface{}) (string, []string, error) {
	ps, foundPS := extensions[patchStrategyOpenapiextensionKey]
	var patchStrategies []string
	var mergeKey, patchStrategy string
	var ok bool
	if foundPS {
		patchStrategy, ok = ps.(string)
		if ok {
			patchStrategies = strings.Split(patchStrategy, ",")
		} else {
			return "", nil, mergepatch.ErrBadArgType(patchStrategy, ps)
		}
	}
	mk, foundMK := extensions[patchMergeKeyOpenapiextensionKey]
	if foundMK {
		mergeKey, ok = mk.(string)
		if !ok {
			return "", nil, mergepatch.ErrBadArgType(mergeKey, mk)
		}
	}
	return mergeKey, patchStrategies, nil
}


func NewUUID() types.UID

func NewUUID() types.UID {
	return types.UID(uuid.New().String())
}


func (v *Error) Error() string

// Error implements the error interface.
func (v *Error) Error() string {
	return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
}


func (v *Error) ErrorBody() string

// ErrorBody returns the error message without the field name.  This is useful
// for building nice-looking higher-level error reporting.
func (v *Error) ErrorBody() string {
	var s string
	switch {
	case v.Type == ErrorTypeRequired:
		s = v.Type.String()
	case v.Type == ErrorTypeForbidden:
		s = v.Type.String()
	case v.Type == ErrorTypeTooLong:
		s = v.Type.String()
	case v.Type == ErrorTypeInternal:
		s = v.Type.String()
	case v.BadValue == omitValue:
		s = v.Type.String()
	default:
		value := v.BadValue
		valueType := reflect.TypeOf(value)
		if value == nil || valueType == nil {
			value = "null"
		} else if valueType.Kind() == reflect.Pointer {
			if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
				value = "null"
			} else {
				value = reflectValue.Elem().Interface()
			}
		}
		switch t := value.(type) {
		case int64, int32, float64, float32, bool:
			// use simple printer for simple types
			s = fmt.Sprintf("%s: %v", v.Type, value)
		case string:
			s = fmt.Sprintf("%s: %q", v.Type, t)
		case fmt.Stringer:
			// anything that defines String() is better than raw struct
			s = fmt.Sprintf("%s: %s", v.Type, t.String())
		default:
			// fallback to raw struct
			// TODO: internal types have panic guards against json.Marshalling to prevent
			// accidental use of internal types in external serialized form.  For now, use
			// %#v, although it would be better to show a more expressive output in the future
			s = fmt.Sprintf("%s: %#v", v.Type, value)
		}
	}
	if len(v.Detail) != 0 {
		s += fmt.Sprintf(": %s", v.Detail)
	}
	return s
}


func (t ErrorType) String() string

// String converts a ErrorType into its corresponding canonical error message.
func (t ErrorType) String() string {
	switch t {
	case ErrorTypeNotFound:
		return "Not found"
	case ErrorTypeRequired:
		return "Required value"
	case ErrorTypeDuplicate:
		return "Duplicate value"
	case ErrorTypeInvalid:
		return "Invalid value"
	case ErrorTypeNotSupported:
		return "Unsupported value"
	case ErrorTypeForbidden:
		return "Forbidden"
	case ErrorTypeTooLong:
		return "Too long"
	case ErrorTypeTooMany:
		return "Too many"
	case ErrorTypeInternal:
		return "Internal error"
	case ErrorTypeTypeInvalid:
		return "Invalid value"
	default:
		panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
	}
}


// TypeInvalid returns a *Error indicating "type is invalid"
func TypeInvalid(field *Path, value interface{}, detail string) *Error {
	return &Error{ErrorTypeTypeInvalid, field.String(), value, detail}
}


// NotFound returns a *Error indicating "value not found".  This is
// used to report failure to find a requested value (e.g. looking up an ID).
func NotFound(field *Path, value interface{}) *Error {
	return &Error{ErrorTypeNotFound, field.String(), value, ""}
}


// Required returns a *Error indicating "value required".  This is used
// to report required values that are not provided (e.g. empty strings, null
// values, or empty arrays).
func Required(field *Path, detail string) *Error {
	return &Error{ErrorTypeRequired, field.String(), "", detail}
}


// Duplicate returns a *Error indicating "duplicate value".  This is
// used to report collisions of values that must be unique (e.g. names or IDs).
func Duplicate(field *Path, value interface{}) *Error {
	return &Error{ErrorTypeDuplicate, field.String(), value, ""}
}


// Invalid returns a *Error indicating "invalid value".  This is used
// to report malformed values (e.g. failed regex match, too long, out of bounds).
func Invalid(field *Path, value interface{}, detail string) *Error {
	return &Error{ErrorTypeInvalid, field.String(), value, detail}
}


// NotSupported returns a *Error indicating "unsupported value".
// This is used to report unknown values for enumerated fields (e.g. a list of
// valid values).
func NotSupported(field *Path, value interface{}, validValues []string) *Error {
	detail := ""
	if len(validValues) > 0 {
		quotedValues := make([]string, len(validValues))
		for i, v := range validValues {
			quotedValues[i] = strconv.Quote(v)
		}
		detail = "supported values: " + strings.Join(quotedValues, ", ")
	}
	return &Error{ErrorTypeNotSupported, field.String(), value, detail}
}


// Forbidden returns a *Error indicating "forbidden".  This is used to
// report valid (as per formatting rules) values which would be accepted under
// some conditions, but which are not permitted by current conditions (e.g.
// security policy).
func Forbidden(field *Path, detail string) *Error {
	return &Error{ErrorTypeForbidden, field.String(), "", detail}
}


// TooLong returns a *Error indicating "too long".  This is used to
// report that the given value is too long.  This is similar to
// Invalid, but the returned error will not include the too-long
// value.
func TooLong(field *Path, value interface{}, maxLength int) *Error {
	return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d bytes", maxLength)}
}


// TooLongMaxLength returns a *Error indicating "too long".  This is used to
// report that the given value is too long.  This is similar to
// Invalid, but the returned error will not include the too-long
// value. If maxLength is negative, no max length will be included in the message.
func TooLongMaxLength(field *Path, value interface{}, maxLength int) *Error {
	var msg string
	if maxLength >= 0 {
		msg = fmt.Sprintf("may not be longer than %d", maxLength)
	} else {
		msg = "value is too long"
	}
	return &Error{ErrorTypeTooLong, field.String(), value, msg}
}


// TooMany returns a *Error indicating "too many". This is used to
// report that a given list has too many items. This is similar to TooLong,
// but the returned error indicates quantity instead of length.
func TooMany(field *Path, actualQuantity, maxQuantity int) *Error {
	var msg string

	if maxQuantity >= 0 {
		msg = fmt.Sprintf("must have at most %d items", maxQuantity)
	} else {
		msg = "has too many items"
	}

	var actual interface{}
	if actualQuantity >= 0 {
		actual = actualQuantity
	} else {
		actual = omitValue
	}

	return &Error{ErrorTypeTooMany, field.String(), actual, msg}
}


// InternalError returns a *Error indicating "internal error".  This is used
// to signal that an error was found that was not directly related to user
// input.  The err argument must be non-nil.
func InternalError(field *Path, err error) *Error {
	return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
}


// NewErrorTypeMatcher returns an errors.Matcher that returns true
// if the provided error is a Error and has the provided ErrorType.
func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
	return func(err error) bool {
		if e, ok := err.(*Error); ok {
			return e.Type == t
		}
		return false
	}
}


// ToAggregate converts the ErrorList into an errors.Aggregate.
func (list ErrorList) ToAggregate() utilerrors.Aggregate {
	if len(list) == 0 {
		return nil
	}
	errs := make([]error, 0, len(list))
	errorMsgs := sets.NewString()
	for _, err := range list {
		msg := fmt.Sprintf("%v", err)
		if errorMsgs.Has(msg) {
			continue
		}
		errorMsgs.Insert(msg)
		errs = append(errs, err)
	}
	return utilerrors.NewAggregate(errs)
}


func fromAggregate(agg utilerrors.Aggregate) ErrorList {
	errs := agg.Errors()
	list := make(ErrorList, len(errs))
	for i := range errs {
		list[i] = errs[i].(*Error)
	}
	return list
}


// Filter removes items from the ErrorList that match the provided fns.
func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
	err := utilerrors.FilterOut(list.ToAggregate(), fns...)
	if err == nil {
		return nil
	}
	// FilterOut takes an Aggregate and returns an Aggregate
	return fromAggregate(err.(utilerrors.Aggregate))
}


func WithPath(p *Path) PathOption

// WithPath generates a PathOption
func WithPath(p *Path) PathOption {
	return func(o *pathOptions) {
		o.path = p
	}
}


func ToPath(opts ...PathOption) *Path

// ToPath produces *Path from a set of PathOption
func ToPath(opts ...PathOption) *Path {
	c := &pathOptions{}
	for _, opt := range opts {
		opt(c)
	}
	return c.path
}


// NewPath creates a root Path object.
func NewPath(name string, moreNames ...string) *Path {
	r := &Path{name: name, parent: nil}
	for _, anotherName := range moreNames {
		r = &Path{name: anotherName, parent: r}
	}
	return r
}


func (p *Path) Root() *Path

// Root returns the root element of this Path.
func (p *Path) Root() *Path {
	for ; p.parent != nil; p = p.parent {
		// Do nothing.
	}
	return p
}


// Child creates a new Path that is a child of the method receiver.
func (p *Path) Child(name string, moreNames ...string) *Path {
	r := NewPath(name, moreNames...)
	r.Root().parent = p
	return r
}


func (p *Path) Index(index int) *Path

// Index indicates that the previous Path is to be subscripted by an int.
// This sets the same underlying value as Key.
func (p *Path) Index(index int) *Path {
	return &Path{index: strconv.Itoa(index), parent: p}
}


func (p *Path) Key(key string) *Path

// Key indicates that the previous Path is to be subscripted by a string.
// This sets the same underlying value as Index.
func (p *Path) Key(key string) *Path {
	return &Path{index: key, parent: p}
}


func (p *Path) String() string

// String produces a string representation of the Path.
func (p *Path) String() string {
	if p == nil {
		return "<nil>"
	}
	// make a slice to iterate
	elems := []*Path{}
	for ; p != nil; p = p.parent {
		elems = append(elems, p)
	}

	// iterate, but it has to be backwards
	buf := bytes.NewBuffer(nil)
	for i := range elems {
		p := elems[len(elems)-1-i]
		if p.parent != nil && len(p.name) > 0 {
			// This is either the root or it is a subscript.
			buf.WriteString(".")
		}
		if len(p.name) > 0 {
			buf.WriteString(p.name)
		} else {
			fmt.Fprintf(buf, "[%s]", p.index)
		}
	}
	return buf.String()
}


// IsQualifiedName tests whether the value passed is what Kubernetes calls a
// "qualified name".  This is a format used in various places throughout the
// system.  If the value is not valid, a list of error strings is returned.
// Otherwise an empty list (or nil) is returned.
func IsQualifiedName(value string) []string {
	var errs []string
	parts := strings.Split(value, "/")
	var name string
	switch len(parts) {
	case 1:
		name = parts[0]
	case 2:
		var prefix string
		prefix, name = parts[0], parts[1]
		if len(prefix) == 0 {
			errs = append(errs, "prefix part "+EmptyError())
		} else if msgs := IsDNS1123Subdomain(prefix); len(msgs) != 0 {
			errs = append(errs, prefixEach(msgs, "prefix part ")...)
		}
	default:
		return append(errs, "a qualified name "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc")+
			" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')")
	}

	if len(name) == 0 {
		errs = append(errs, "name part "+EmptyError())
	} else if len(name) > qualifiedNameMaxLength {
		errs = append(errs, "name part "+MaxLenError(qualifiedNameMaxLength))
	}
	if !qualifiedNameRegexp.MatchString(name) {
		errs = append(errs, "name part "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc"))
	}
	return errs
}


// IsFullyQualifiedName checks if the name is fully qualified. This is similar
// to IsFullyQualifiedDomainName but requires a minimum of 3 segments instead of
// 2 and does not accept a trailing . as valid.
// TODO: This function is deprecated and preserved until all callers migrate to
// IsFullyQualifiedDomainName; please don't add new callers.
func IsFullyQualifiedName(fldPath *field.Path, name string) field.ErrorList {
	var allErrors field.ErrorList
	if len(name) == 0 {
		return append(allErrors, field.Required(fldPath, ""))
	}
	if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
		return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
	}
	if len(strings.Split(name, ".")) < 3 {
		return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least three segments separated by dots"))
	}
	return allErrors
}


// IsFullyQualifiedDomainName checks if the domain name is fully qualified. This
// is similar to IsFullyQualifiedName but only requires a minimum of 2 segments
// instead of 3 and accepts a trailing . as valid.
func IsFullyQualifiedDomainName(fldPath *field.Path, name string) field.ErrorList {
	var allErrors field.ErrorList
	if len(name) == 0 {
		return append(allErrors, field.Required(fldPath, ""))
	}
	if strings.HasSuffix(name, ".") {
		name = name[:len(name)-1]
	}
	if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
		return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
	}
	if len(strings.Split(name, ".")) < 2 {
		return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least two segments separated by dots"))
	}
	for _, label := range strings.Split(name, ".") {
		if errs := IsDNS1123Label(label); len(errs) > 0 {
			return append(allErrors, field.Invalid(fldPath, label, strings.Join(errs, ",")))
		}
	}
	return allErrors
}


// IsDomainPrefixedPath checks if the given string is a domain-prefixed path
// (e.g. acme.io/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
func IsDomainPrefixedPath(fldPath *field.Path, dpPath string) field.ErrorList {
	var allErrs field.ErrorList
	if len(dpPath) == 0 {
		return append(allErrs, field.Required(fldPath, ""))
	}

	segments := strings.SplitN(dpPath, "/", 2)
	if len(segments) != 2 || len(segments[0]) == 0 || len(segments[1]) == 0 {
		return append(allErrs, field.Invalid(fldPath, dpPath, "must be a domain-prefixed path (such as \"acme.io/foo\")"))
	}

	host := segments[0]
	for _, err := range IsDNS1123Subdomain(host) {
		allErrs = append(allErrs, field.Invalid(fldPath, host, err))
	}

	path := segments[1]
	if !httpPathRegexp.MatchString(path) {
		return append(allErrs, field.Invalid(fldPath, path, RegexError("Invalid path", httpPathFmt)))
	}

	return allErrs
}


// IsValidLabelValue tests whether the value passed is a valid label value.  If
// the value is not valid, a list of error strings is returned.  Otherwise an
// empty list (or nil) is returned.
func IsValidLabelValue(value string) []string {
	var errs []string
	if len(value) > LabelValueMaxLength {
		errs = append(errs, MaxLenError(LabelValueMaxLength))
	}
	if !labelValueRegexp.MatchString(value) {
		errs = append(errs, RegexError(labelValueErrMsg, labelValueFmt, "MyValue", "my_value", "12345"))
	}
	return errs
}


// IsDNS1123Label tests for a string that conforms to the definition of a label in
// DNS (RFC 1123).
func IsDNS1123Label(value string) []string {
	var errs []string
	if len(value) > DNS1123LabelMaxLength {
		errs = append(errs, MaxLenError(DNS1123LabelMaxLength))
	}
	if !dns1123LabelRegexp.MatchString(value) {
		if dns1123SubdomainRegexp.MatchString(value) {
			// It was a valid subdomain and not a valid label.  Since we
			// already checked length, it must be dots.
			errs = append(errs, "must not contain dots")
		} else {
			errs = append(errs, RegexError(dns1123LabelErrMsg, dns1123LabelFmt, "my-name", "123-abc"))
		}
	}
	return errs
}


// IsDNS1123Subdomain tests for a string that conforms to the definition of a
// subdomain in DNS (RFC 1123).
func IsDNS1123Subdomain(value string) []string {
	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !dns1123SubdomainRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1123SubdomainErrorMsg, dns1123SubdomainFmt, "example.com"))
	}
	return errs
}


// IsDNS1035Label tests for a string that conforms to the definition of a label in
// DNS (RFC 1035).
func IsDNS1035Label(value string) []string {
	var errs []string
	if len(value) > DNS1035LabelMaxLength {
		errs = append(errs, MaxLenError(DNS1035LabelMaxLength))
	}
	if !dns1035LabelRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1035LabelErrMsg, dns1035LabelFmt, "my-name", "abc-123"))
	}
	return errs
}


// IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a
// wildcard subdomain in DNS (RFC 1034 section 4.3.3).
func IsWildcardDNS1123Subdomain(value string) []string {
	wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^" + wildcardDNS1123SubdomainFmt + "$")

	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !wildcardDNS1123SubdomainRegexp.MatchString(value) {
		errs = append(errs, RegexError(wildcardDNS1123SubdomainErrMsg, wildcardDNS1123SubdomainFmt, "*.example.com"))
	}
	return errs
}


// IsCIdentifier tests for a string that conforms the definition of an identifier
// in C. This checks the format, but not the length.
func IsCIdentifier(value string) []string {
	if !cIdentifierRegexp.MatchString(value) {
		return []string{RegexError(identifierErrMsg, cIdentifierFmt, "my_name", "MY_NAME", "MyName")}
	}
	return nil
}


func IsValidPortNum(port int) []string

// IsValidPortNum tests that the argument is a valid, non-zero port number.
func IsValidPortNum(port int) []string {
	if 1 <= port && port <= 65535 {
		return nil
	}
	return []string{InclusiveRangeError(1, 65535)}
}


// IsInRange tests that the argument is in an inclusive range.
func IsInRange(value int, min int, max int) []string {
	if value >= min && value <= max {
		return nil
	}
	return []string{InclusiveRangeError(min, max)}
}


func IsValidGroupID(gid int64) []string

// IsValidGroupID tests that the argument is a valid Unix GID.
func IsValidGroupID(gid int64) []string {
	if minGroupID <= gid && gid <= maxGroupID {
		return nil
	}
	return []string{InclusiveRangeError(minGroupID, maxGroupID)}
}


func IsValidUserID(uid int64) []string

// IsValidUserID tests that the argument is a valid Unix UID.
func IsValidUserID(uid int64) []string {
	if minUserID <= uid && uid <= maxUserID {
		return nil
	}
	return []string{InclusiveRangeError(minUserID, maxUserID)}
}


// IsValidPortName check that the argument is valid syntax. It must be
// non-empty and no more than 15 characters long. It may contain only [-a-z0-9]
// and must contain at least one letter [a-z]. It must not start or end with a
// hyphen, nor contain adjacent hyphens.
//
// Note: We only allow lower-case characters, even though RFC 6335 is case
// insensitive.
func IsValidPortName(port string) []string {
	var errs []string
	if len(port) > 15 {
		errs = append(errs, MaxLenError(15))
	}
	if !portNameCharsetRegex.MatchString(port) {
		errs = append(errs, "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)")
	}
	if !portNameOneLetterRegexp.MatchString(port) {
		errs = append(errs, "must contain at least one letter (a-z)")
	}
	if strings.Contains(port, "--") {
		errs = append(errs, "must not contain consecutive hyphens")
	}
	if len(port) > 0 && (port[0] == '-' || port[len(port)-1] == '-') {
		errs = append(errs, "must not begin or end with a hyphen")
	}
	return errs
}


func IsValidIP(value string) []string

// IsValidIP tests that the argument is a valid IP address.
func IsValidIP(value string) []string {
	if netutils.ParseIPSloppy(value) == nil {
		return []string{"must be a valid IP address, (e.g. 10.9.8.7 or 2001:db8::ffff)"}
	}
	return nil
}


// IsValidIPv4Address tests that the argument is a valid IPv4 address.
func IsValidIPv4Address(fldPath *field.Path, value string) field.ErrorList {
	var allErrors field.ErrorList
	ip := netutils.ParseIPSloppy(value)
	if ip == nil || ip.To4() == nil {
		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv4 address"))
	}
	return allErrors
}


// IsValidIPv6Address tests that the argument is a valid IPv6 address.
func IsValidIPv6Address(fldPath *field.Path, value string) field.ErrorList {
	var allErrors field.ErrorList
	ip := netutils.ParseIPSloppy(value)
	if ip == nil || ip.To4() != nil {
		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv6 address"))
	}
	return allErrors
}


// IsValidPercent checks that string is in the form of a percentage
func IsValidPercent(percent string) []string {
	if !percentRegexp.MatchString(percent) {
		return []string{RegexError(percentErrMsg, percentFmt, "1%", "93%")}
	}
	return nil
}


// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
// definition of a valid header field name (a stricter subset than RFC7230).
func IsHTTPHeaderName(value string) []string {
	if !httpHeaderNameRegexp.MatchString(value) {
		return []string{RegexError(httpHeaderNameErrMsg, httpHeaderNameFmt, "X-Header-Name")}
	}
	return nil
}


// IsEnvVarName tests if a string is a valid environment variable name.
func IsEnvVarName(value string) []string {
	var errs []string
	if !envVarNameRegexp.MatchString(value) {
		errs = append(errs, RegexError(envVarNameFmtErrMsg, envVarNameFmt, "my.env-name", "MY_ENV.NAME", "MyEnvName1"))
	}

	errs = append(errs, hasChDirPrefix(value)...)
	return errs
}


// IsConfigMapKey tests for a string that is a valid key for a ConfigMap or Secret
func IsConfigMapKey(value string) []string {
	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !configMapKeyRegexp.MatchString(value) {
		errs = append(errs, RegexError(configMapKeyErrMsg, configMapKeyFmt, "key.name", "KEY_NAME", "key-name"))
	}
	errs = append(errs, hasChDirPrefix(value)...)
	return errs
}


func MaxLenError(length int) string

// MaxLenError returns a string explanation of a "string too long" validation
// failure.
func MaxLenError(length int) string {
	return fmt.Sprintf("must be no more than %d characters", length)
}


// RegexError returns a string explanation of a regex validation failure.
func RegexError(msg string, fmt string, examples ...string) string {
	if len(examples) == 0 {
		return msg + " (regex used for validation is '" + fmt + "')"
	}
	msg += " (e.g. "
	for i := range examples {
		if i > 0 {
			msg += " or "
		}
		msg += "'" + examples[i] + "', "
	}
	msg += "regex used for validation is '" + fmt + "')"
	return msg
}


func EmptyError() string

// EmptyError returns a string explanation of a "must not be empty" validation
// failure.
func EmptyError() string {
	return "must be non-empty"
}


func prefixEach(msgs []string, prefix string) []string {
	for i := range msgs {
		msgs[i] = prefix + msgs[i]
	}
	return msgs
}


// InclusiveRangeError returns a string explanation of a numeric "must be
// between" validation failure.
func InclusiveRangeError(lo, hi int) string {
	return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
}


func hasChDirPrefix(value string) []string {
	var errs []string
	switch {
	case value == ".":
		errs = append(errs, `must not be '.'`)
	case value == "..":
		errs = append(errs, `must not be '..'`)
	case strings.HasPrefix(value, ".."):
		errs = append(errs, `must not start with '..'`)
	}
	return errs
}


// IsValidSocketAddr checks that string represents a valid socket address
// as defined in RFC 789. (e.g 0.0.0.0:10254 or [::]:10254))
func IsValidSocketAddr(value string) []string {
	var errs []string
	ip, port, err := net.SplitHostPort(value)
	if err != nil {
		errs = append(errs, "must be a valid socket address format, (e.g. 0.0.0.0:10254 or [::]:10254)")
		return errs
	}
	portInt, _ := strconv.Atoi(port)
	errs = append(errs, IsValidPortNum(portInt)...)
	errs = append(errs, IsValidIP(ip)...)
	return errs
}


func parse(str string, semver bool) (*Version, error) {
	parts := versionMatchRE.FindStringSubmatch(str)
	if parts == nil {
		return nil, fmt.Errorf("could not parse %q as version", str)
	}
	numbers, extra := parts[1], parts[2]

	components := strings.Split(numbers, ".")
	if (semver && len(components) != 3) || (!semver && len(components) < 2) {
		return nil, fmt.Errorf("illegal version string %q", str)
	}

	v := &Version{
		components: make([]uint, len(components)),
		semver:     semver,
	}
	for i, comp := range components {
		if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
			return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
		}
		num, err := strconv.ParseUint(comp, 10, 0)
		if err != nil {
			return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
		}
		v.components[i] = uint(num)
	}

	if semver && extra != "" {
		extraParts := extraMatchRE.FindStringSubmatch(extra)
		if extraParts == nil {
			return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
		}
		v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]

		for _, comp := range strings.Split(v.preRelease, ".") {
			if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
				if strings.HasPrefix(comp, "0") && comp != "0" {
					return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
				}
			}
		}
	}

	return v, nil
}


// ParseGeneric parses a "generic" version string. The version string must consist of two
// or more dot-separated numeric fields (the first of which can't have leading zeroes),
// followed by arbitrary uninterpreted data (which need not be separated from the final
// numeric field by punctuation). For convenience, leading and trailing whitespace is
// ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
func ParseGeneric(str string) (*Version, error) {
	return parse(str, false)
}


// MustParseGeneric is like ParseGeneric except that it panics on error
func MustParseGeneric(str string) *Version {
	v, err := ParseGeneric(str)
	if err != nil {
		panic(err)
	}
	return v
}


// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
// leading and trailing whitespace, and allows the version to be preceded by "v"). For
// version strings that are not guaranteed to obey the Semantic Versioning syntax, use
// ParseGeneric.
func ParseSemantic(str string) (*Version, error) {
	return parse(str, true)
}


// MustParseSemantic is like ParseSemantic except that it panics on error
func MustParseSemantic(str string) *Version {
	v, err := ParseSemantic(str)
	if err != nil {
		panic(err)
	}
	return v
}


func (v *Version) Major() uint

// Major returns the major release number
func (v *Version) Major() uint {
	return v.components[0]
}


func (v *Version) Minor() uint

// Minor returns the minor release number
func (v *Version) Minor() uint {
	return v.components[1]
}


func (v *Version) Patch() uint

// Patch returns the patch release number if v is a Semantic Version, or 0
func (v *Version) Patch() uint {
	if len(v.components) < 3 {
		return 0
	}
	return v.components[2]
}


// BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
func (v *Version) BuildMetadata() string {
	return v.buildMetadata
}


func (v *Version) PreRelease() string

// PreRelease returns the prerelease metadata, if v is a Semantic Version, or ""
func (v *Version) PreRelease() string {
	return v.preRelease
}


func (v *Version) Components() []uint

// Components returns the version number components
func (v *Version) Components() []uint {
	return v.components
}


// WithMajor returns copy of the version object with requested major number
func (v *Version) WithMajor(major uint) *Version {
	result := *v
	result.components = []uint{major, v.Minor(), v.Patch()}
	return &result
}


// WithMinor returns copy of the version object with requested minor number
func (v *Version) WithMinor(minor uint) *Version {
	result := *v
	result.components = []uint{v.Major(), minor, v.Patch()}
	return &result
}


// WithPatch returns copy of the version object with requested patch number
func (v *Version) WithPatch(patch uint) *Version {
	result := *v
	result.components = []uint{v.Major(), v.Minor(), patch}
	return &result
}


// WithPreRelease returns copy of the version object with requested prerelease
func (v *Version) WithPreRelease(preRelease string) *Version {
	result := *v
	result.components = []uint{v.Major(), v.Minor(), v.Patch()}
	result.preRelease = preRelease
	return &result
}


// WithBuildMetadata returns copy of the version object with requested buildMetadata
func (v *Version) WithBuildMetadata(buildMetadata string) *Version {
	result := *v
	result.components = []uint{v.Major(), v.Minor(), v.Patch()}
	result.buildMetadata = buildMetadata
	return &result
}


func (v *Version) String() string

// String converts a Version back to a string; note that for versions parsed with
// ParseGeneric, this will not include the trailing uninterpreted portion of the version
// number.
func (v *Version) String() string {
	if v == nil {
		return "<nil>"
	}
	var buffer bytes.Buffer

	for i, comp := range v.components {
		if i > 0 {
			buffer.WriteString(".")
		}
		buffer.WriteString(fmt.Sprintf("%d", comp))
	}
	if v.preRelease != "" {
		buffer.WriteString("-")
		buffer.WriteString(v.preRelease)
	}
	if v.buildMetadata != "" {
		buffer.WriteString("+")
		buffer.WriteString(v.buildMetadata)
	}

	return buffer.String()
}


// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
// if they are equal
func (v *Version) compareInternal(other *Version) int {

	vLen := len(v.components)
	oLen := len(other.components)
	for i := 0; i < vLen && i < oLen; i++ {
		switch {
		case other.components[i] < v.components[i]:
			return 1
		case other.components[i] > v.components[i]:
			return -1
		}
	}

	// If components are common but one has more items and they are not zeros, it is bigger
	switch {
	case oLen < vLen && !onlyZeros(v.components[oLen:]):
		return 1
	case oLen > vLen && !onlyZeros(other.components[vLen:]):
		return -1
	}

	if !v.semver || !other.semver {
		return 0
	}

	switch {
	case v.preRelease == "" && other.preRelease != "":
		return 1
	case v.preRelease != "" && other.preRelease == "":
		return -1
	case v.preRelease == other.preRelease: // includes case where both are ""
		return 0
	}

	vPR := strings.Split(v.preRelease, ".")
	oPR := strings.Split(other.preRelease, ".")
	for i := 0; i < len(vPR) && i < len(oPR); i++ {
		vNum, err := strconv.ParseUint(vPR[i], 10, 0)
		if err == nil {
			oNum, err := strconv.ParseUint(oPR[i], 10, 0)
			if err == nil {
				switch {
				case oNum < vNum:
					return 1
				case oNum > vNum:
					return -1
				default:
					continue
				}
			}
		}
		if oPR[i] < vPR[i] {
			return 1
		} else if oPR[i] > vPR[i] {
			return -1
		}
	}

	switch {
	case len(oPR) < len(vPR):
		return 1
	case len(oPR) > len(vPR):
		return -1
	}

	return 0
}


func onlyZeros(array []uint) bool

// returns false if array contain any non-zero element
func onlyZeros(array []uint) bool {
	for _, num := range array {
		if num != 0 {
			return false
		}
	}
	return true
}


// AtLeast tests if a version is at least equal to a given minimum version. If both
// Versions are Semantic Versions, this will use the Semantic Version comparison
// algorithm. Otherwise, it will compare only the numeric components, with non-present
// components being considered "0" (ie, "1.4" is equal to "1.4.0").
func (v *Version) AtLeast(min *Version) bool {
	return v.compareInternal(min) != -1
}


// LessThan tests if a version is less than a given version. (It is exactly the opposite
// of AtLeast, for situations where asking "is v too old?" makes more sense than asking
// "is v new enough?".)
func (v *Version) LessThan(other *Version) bool {
	return v.compareInternal(other) == -1
}


// Compare compares v against a version string (which will be parsed as either Semantic
// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
// it is greater than other, or 0 if they are equal.
func (v *Version) Compare(other string) (int, error) {
	ov, err := parse(other, v.semver)
	if err != nil {
		return 0, err
	}
	return v.compareInternal(ov), nil
}


func (b *Backoff) Step() time.Duration

// Step returns an amount of time to sleep determined by the original
// Duration and Jitter. The backoff is mutated to update its Steps and
// Duration. A nil Backoff always has a zero-duration step.
func (b *Backoff) Step() time.Duration {
	if b == nil {
		return 0
	}
	var nextDuration time.Duration
	nextDuration, b.Duration, b.Steps = delay(b.Steps, b.Duration, b.Cap, b.Factor, b.Jitter)
	return nextDuration
}


func (b Backoff) DelayFunc() DelayFunc

// DelayFunc returns a function that will compute the next interval to
// wait given the arguments in b. It does not mutate the original backoff
// but the function is safe to use only from a single goroutine.
func (b Backoff) DelayFunc() DelayFunc {
	steps := b.Steps
	duration := b.Duration
	cap := b.Cap
	factor := b.Factor
	jitter := b.Jitter

	return func() time.Duration {
		var nextDuration time.Duration
		// jitter is applied per step and is not cumulative over multiple steps
		nextDuration, duration, steps = delay(steps, duration, cap, factor, jitter)
		return nextDuration
	}
}


func (b Backoff) Timer() Timer

// Timer returns a timer implementation appropriate to this backoff's parameters
// for use with wait functions.
func (b Backoff) Timer() Timer {
	if b.Steps > 1 || b.Jitter != 0 {
		return &variableTimer{new: internalClock.NewTimer, fn: b.DelayFunc()}
	}
	if b.Duration > 0 {
		return &fixedTimer{new: internalClock.NewTicker, interval: b.Duration}
	}
	return newNoopTimer()
}


// delay implements the core delay algorithm used in this package.
func delay(steps int, duration, cap time.Duration, factor, jitter float64) (_ time.Duration, next time.Duration, nextSteps int) {
	// when steps is non-positive, do not alter the base duration
	if steps < 1 {
		if jitter > 0 {
			return Jitter(duration, jitter), duration, 0
		}
		return duration, duration, 0
	}
	steps--

	// calculate the next step's interval
	if factor != 0 {
		next = time.Duration(float64(duration) * factor)
		if cap > 0 && next > cap {
			next = cap
			steps = 0
		}
	} else {
		next = duration
	}

	// add jitter for this step
	if jitter > 0 {
		duration = Jitter(duration, jitter)
	}

	return duration, next, steps

}


// DelayWithReset returns a DelayFunc that will return the appropriate next interval to
// wait. Every resetInterval the backoff parameters are reset to their initial state.
// This method is safe to invoke from multiple goroutines, but all calls will advance
// the backoff state when Factor is set. If Factor is zero, this method is the same as
// invoking b.DelayFunc() since Steps has no impact without Factor. If resetInterval is
// zero no backoff will be performed as the same calling DelayFunc with a zero factor
// and steps.
func (b Backoff) DelayWithReset(c clock.Clock, resetInterval time.Duration) DelayFunc {
	if b.Factor <= 0 {
		return b.DelayFunc()
	}
	if resetInterval <= 0 {
		b.Steps = 0
		b.Factor = 0
		return b.DelayFunc()
	}
	return (&backoffManager{
		backoff:        b,
		initialBackoff: b,
		resetInterval:  resetInterval,

		clock:     c,
		lastStart: c.Now(),
		timer:     nil,
	}).Step
}


// Until loops until stop channel is closed, running f every period.
//
// Until is syntactic sugar on top of JitterUntil with zero jitter factor and
// with sliding = true (which means the timer for period starts after the f
// completes).
func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
	JitterUntil(f, period, 0.0, true, stopCh)
}


// UntilWithContext loops until context is done, running f every period.
//
// UntilWithContext is syntactic sugar on top of JitterUntilWithContext
// with zero jitter factor and with sliding = true (which means the timer
// for period starts after the f completes).
func UntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) {
	JitterUntilWithContext(ctx, f, period, 0.0, true)
}


// NonSlidingUntil loops until stop channel is closed, running f every
// period.
//
// NonSlidingUntil is syntactic sugar on top of JitterUntil with zero jitter
// factor, with sliding = false (meaning the timer for period starts at the same
// time as the function starts).
func NonSlidingUntil(f func(), period time.Duration, stopCh <-chan struct{}) {
	JitterUntil(f, period, 0.0, false, stopCh)
}


// NonSlidingUntilWithContext loops until context is done, running f every
// period.
//
// NonSlidingUntilWithContext is syntactic sugar on top of JitterUntilWithContext
// with zero jitter factor, with sliding = false (meaning the timer for period
// starts at the same time as the function starts).
func NonSlidingUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) {
	JitterUntilWithContext(ctx, f, period, 0.0, false)
}


// JitterUntil loops until stop channel is closed, running f every period.
//
// If jitterFactor is positive, the period is jittered before every run of f.
// If jitterFactor is not positive, the period is unchanged and not jittered.
//
// If sliding is true, the period is computed after f runs. If it is false then
// period includes the runtime for f.
//
// Close stopCh to stop. f may not be invoked if stop channel is already
// closed. Pass NeverStop to if you don't want it stop.
func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) {
	BackoffUntil(f, NewJitteredBackoffManager(period, jitterFactor, &clock.RealClock{}), sliding, stopCh)
}


// BackoffUntil loops until stop channel is closed, run f every duration given by BackoffManager.
//
// If sliding is true, the period is computed after f runs. If it is false then
// period includes the runtime for f.
func BackoffUntil(f func(), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) {
	var t clock.Timer
	for {
		select {
		case <-stopCh:
			return
		default:
		}

		if !sliding {
			t = backoff.Backoff()
		}

		func() {
			defer runtime.HandleCrash()
			f()
		}()

		if sliding {
			t = backoff.Backoff()
		}

		// NOTE: b/c there is no priority selection in golang
		// it is possible for this to race, meaning we could
		// trigger t.C and stopCh, and t.C select falls through.
		// In order to mitigate we re-check stopCh at the beginning
		// of every loop to prevent extra executions of f().
		select {
		case <-stopCh:
			if !t.Stop() {
				<-t.C()
			}
			return
		case <-t.C():
		}
	}
}


// JitterUntilWithContext loops until context is done, running f every period.
//
// If jitterFactor is positive, the period is jittered before every run of f.
// If jitterFactor is not positive, the period is unchanged and not jittered.
//
// If sliding is true, the period is computed after f runs. If it is false then
// period includes the runtime for f.
//
// Cancel context to stop. f may not be invoked if context is already expired.
func JitterUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration, jitterFactor float64, sliding bool) {
	JitterUntil(func() { f(ctx) }, period, jitterFactor, sliding, ctx.Done())
}


// Step returns the expected next duration to wait.
func (b *backoffManager) Step() time.Duration {
	b.lock.Lock()
	defer b.lock.Unlock()

	switch {
	case b.resetInterval == 0:
		b.backoff = b.initialBackoff
	case b.clock.Now().Sub(b.lastStart) > b.resetInterval:
		b.backoff = b.initialBackoff
		b.lastStart = b.clock.Now()
	}
	return b.backoff.Step()
}


// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer
// for exponential backoff. The returned timer must be drained before calling Backoff() the second
// time.
func (b *backoffManager) Backoff() clock.Timer {
	b.lock.Lock()
	defer b.lock.Unlock()
	if b.timer == nil {
		b.timer = b.clock.NewTimer(b.Step())
	} else {
		b.timer.Reset(b.Step())
	}
	return b.timer
}


func (b *backoffManager) Timer() Timer

// Timer returns a new Timer instance that shares the clock and the reset behavior with all other
// timers.
func (b *backoffManager) Timer() Timer {
	return DelayFunc(b.Step).Timer(b.clock)
}


// NewExponentialBackoffManager returns a manager for managing exponential backoff. Each backoff is jittered and
// backoff will not exceed the given max. If the backoff is not called within resetDuration, the backoff is reset.
// This backoff manager is used to reduce load during upstream unhealthiness.
//
// Deprecated: Will be removed when the legacy Poll methods are removed. Callers should construct a
// Backoff struct, use DelayWithReset() to get a DelayFunc that periodically resets itself, and then
// invoke Timer() when calling wait.BackoffUntil.
//
// Instead of:
//
//	bm := wait.NewExponentialBackoffManager(init, max, reset, factor, jitter, clock)
//	...
//	wait.BackoffUntil(..., bm.Backoff, ...)
//
// Use:
//
//	delayFn := wait.Backoff{
//	  Duration: init,
//	  Cap:      max,
//	  Steps:    int(math.Ceil(float64(max) / float64(init))), // now a required argument
//	  Factor:   factor,
//	  Jitter:   jitter,
//	}.DelayWithReset(reset, clock)
//	wait.BackoffUntil(..., delayFn.Timer(), ...)
func NewExponentialBackoffManager(initBackoff, maxBackoff, resetDuration time.Duration, backoffFactor, jitter float64, c clock.Clock) BackoffManager {
	return &exponentialBackoffManagerImpl{
		backoff: &Backoff{
			Duration: initBackoff,
			Factor:   backoffFactor,
			Jitter:   jitter,

			// the current impl of wait.Backoff returns Backoff.Duration once steps are used up, which is not
			// what we ideally need here, we set it to max int and assume we will never use up the steps
			Steps: math.MaxInt32,
			Cap:   maxBackoff,
		},
		backoffTimer:         nil,
		initialBackoff:       initBackoff,
		lastBackoffStart:     c.Now(),
		backoffResetDuration: resetDuration,
		clock:                c,
	}
}


func (b *exponentialBackoffManagerImpl) getNextBackoff() time.Duration {
	if b.clock.Now().Sub(b.lastBackoffStart) > b.backoffResetDuration {
		b.backoff.Steps = math.MaxInt32
		b.backoff.Duration = b.initialBackoff
	}
	b.lastBackoffStart = b.clock.Now()
	return b.backoff.Step()
}


// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for exponential backoff.
// The returned timer must be drained before calling Backoff() the second time
func (b *exponentialBackoffManagerImpl) Backoff() clock.Timer {
	if b.backoffTimer == nil {
		b.backoffTimer = b.clock.NewTimer(b.getNextBackoff())
	} else {
		b.backoffTimer.Reset(b.getNextBackoff())
	}
	return b.backoffTimer
}


// NewJitteredBackoffManager returns a BackoffManager that backoffs with given duration plus given jitter. If the jitter
// is negative, backoff will not be jittered.
//
// Deprecated: Will be removed when the legacy Poll methods are removed. Callers should construct a
// Backoff struct and invoke Timer() when calling wait.BackoffUntil.
//
// Instead of:
//
//	bm := wait.NewJitteredBackoffManager(duration, jitter, clock)
//	...
//	wait.BackoffUntil(..., bm.Backoff, ...)
//
// Use:
//
//	wait.BackoffUntil(..., wait.Backoff{Duration: duration, Jitter: jitter}.Timer(), ...)
func NewJitteredBackoffManager(duration time.Duration, jitter float64, c clock.Clock) BackoffManager {
	return &jitteredBackoffManagerImpl{
		clock:        c,
		duration:     duration,
		jitter:       jitter,
		backoffTimer: nil,
	}
}


func (j *jitteredBackoffManagerImpl) getNextBackoff() time.Duration {
	jitteredPeriod := j.duration
	if j.jitter > 0.0 {
		jitteredPeriod = Jitter(j.duration, j.jitter)
	}
	return jitteredPeriod
}


// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for jittered backoff.
// The returned timer must be drained before calling Backoff() the second time
func (j *jitteredBackoffManagerImpl) Backoff() clock.Timer {
	backoff := j.getNextBackoff()
	if j.backoffTimer == nil {
		j.backoffTimer = j.clock.NewTimer(backoff)
	} else {
		j.backoffTimer.Reset(backoff)
	}
	return j.backoffTimer
}


// ExponentialBackoff repeats a condition check with exponential backoff.
//
// It repeatedly checks the condition and then sleeps, using `backoff.Step()`
// to determine the length of the sleep and adjust Duration and Steps.
// Stops and returns as soon as:
// 1. the condition check returns true or an error,
// 2. `backoff.Steps` checks of the condition have been done, or
// 3. a sleep truncated by the cap on duration has been completed.
// In case (1) the returned error is what the condition function returned.
// In all other cases, ErrWaitTimeout is returned.
//
// Since backoffs are often subject to cancellation, we recommend using
// ExponentialBackoffWithContext and passing a context to the method.
func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
	for backoff.Steps > 0 {
		if ok, err := runConditionWithCrashProtection(condition); err != nil || ok {
			return err
		}
		if backoff.Steps == 1 {
			break
		}
		time.Sleep(backoff.Step())
	}
	return ErrWaitTimeout
}


// ExponentialBackoffWithContext repeats a condition check with exponential backoff.
// It immediately returns an error if the condition returns an error, the context is cancelled
// or hits the deadline, or if the maximum attempts defined in backoff is exceeded (ErrWaitTimeout).
// If an error is returned by the condition the backoff stops immediately. The condition will
// never be invoked more than backoff.Steps times.
func ExponentialBackoffWithContext(ctx context.Context, backoff Backoff, condition ConditionWithContextFunc) error {
	for backoff.Steps > 0 {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		if ok, err := runConditionWithCrashProtectionWithContext(ctx, condition); err != nil || ok {
			return err
		}

		if backoff.Steps == 1 {
			break
		}

		waitBeforeRetry := backoff.Step()
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-time.After(waitBeforeRetry):
		}
	}

	return ErrWaitTimeout
}


// Timer takes an arbitrary delay function and returns a timer that can handle arbitrary interval changes.
// Use Backoff{...}.Timer() for simple delays and more efficient timers.
func (fn DelayFunc) Timer(c clock.Clock) Timer {
	return &variableTimer{fn: fn, new: c.NewTimer}
}


// Until takes an arbitrary delay function and runs until cancelled or the condition indicates exit. This
// offers all of the functionality of the methods in this package.
func (fn DelayFunc) Until(ctx context.Context, immediate, sliding bool, condition ConditionWithContextFunc) error {
	return loopConditionUntilContext(ctx, &variableTimer{fn: fn, new: internalClock.NewTimer}, immediate, sliding, condition)
}


// Concurrent returns a version of this DelayFunc that is safe for use by multiple goroutines that
// wish to share a single delay timer.
func (fn DelayFunc) Concurrent() DelayFunc {
	var lock sync.Mutex
	return func() time.Duration {
		lock.Lock()
		defer lock.Unlock()
		return fn()
	}
}


func Interrupted(err error) bool

// Interrupted returns true if the error indicates a Poll, ExponentialBackoff, or
// Until loop exited for any reason besides the condition returning true or an
// error. A loop is considered interrupted if the calling context is cancelled,
// the context reaches its deadline, or a backoff reaches its maximum allowed
// steps.
//
// Callers should use this method instead of comparing the error value directly to
// ErrWaitTimeout, as methods that cancel a context may not return that error.
//
// Instead of:
//
//	err := wait.Poll(...)
//	if err == wait.ErrWaitTimeout {
//	    log.Infof("Wait for operation exceeded")
//	} else ...
//
// Use:
//
//	err := wait.Poll(...)
//	if wait.Interrupted(err) {
//	    log.Infof("Wait for operation exceeded")
//	} else ...
func Interrupted(err error) bool {
	switch {
	case errors.Is(err, errWaitTimeout),
		errors.Is(err, context.Canceled),
		errors.Is(err, context.DeadlineExceeded):
		return true
	default:
		return false
	}
}


// ErrorInterrupted returns an error that indicates the wait was ended
// early for a given reason. If no cause is provided a generic error
// will be used but callers are encouraged to provide a real cause for
// clarity in debugging.
func ErrorInterrupted(cause error) error {
	switch cause.(type) {
	case errInterrupted:
		// no need to wrap twice since errInterrupted is only needed
		// once in a chain
		return cause
	default:
		return errInterrupted{cause}
	}
}


func (e errInterrupted) Unwrap() error        { return e.cause }


func (e errInterrupted) Is(target error) bool { return target == errWaitTimeout }


func (e errInterrupted) Error() string

func (e errInterrupted) Error() string {
	if e.cause == nil {
		// returns the same error message as historical behavior
		return "timed out waiting for the condition"
	}
	return e.cause.Error()
}