func CalculateOriginalIndex(childIdx int, toCache *bitarray.BitSet) int {
	var sum = 0
	for i := uint(0); i < toCache.Cap(); i++ {
		if toCache.IsSet(i) {
			sum++
		}
		if childIdx+1 == sum {
			return int(i)
		}
	}
	return -1
}
Compute the original index of a sub-task.
package core
import (
	"context"
	"fmt"
	"time"
	"github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/event"
	"github.com/flyteorg/flytestdlib/errors"
	"github.com/flyteorg/flyteplugins/go/tasks/plugins/array/arraystatus"
	"github.com/flyteorg/flytestdlib/bitarray"
	idlCore "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/core"
	idlPlugins "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/plugins"
	"github.com/flyteorg/flyteplugins/go/tasks/pluginmachinery/core"
	"github.com/flyteorg/flyteplugins/go/tasks/pluginmachinery/utils"
	"github.com/flyteorg/flytestdlib/logger"
	structpb "github.com/golang/protobuf/ptypes/struct"
)
type Phase uint8
const (
	PhaseStart Phase = iota
	PhasePreLaunch
	PhaseLaunch
	PhaseWaitingForResources
	PhaseCheckingSubTaskExecutions
	PhaseAssembleFinalOutput
	PhaseWriteToDiscovery
	PhaseWriteToDiscoveryThenFail
	PhaseSuccess
	PhaseAssembleFinalError
	PhaseRetryableFailure
	PhasePermanentFailure
)
type State struct {
	CurrentPhase         Phase                   `json:"phase"`
	PhaseVersion         uint32                  `json:"phaseVersion"`
	Reason               string                  `json:"reason"`
	ExecutionErr         *idlCore.ExecutionError `json:"err"`
	ExecutionArraySize   int                     `json:"arraySize"`
	OriginalArraySize    int64                   `json:"originalArraySize"`
	ArrayStatus          arraystatus.ArrayStatus `json:"arrayStatus"`
	OriginalMinSuccesses int64                   `json:"minSuccess"`
	IndexesToCache *bitarray.BitSet `json:"indexesToCache"`
}
func (s State) GetReason() string {
	return s.Reason
}
func (s State) GetExecutionArraySize() int {
	return s.ExecutionArraySize
}
func (s State) GetPhase() (phase Phase, version uint32) {
	return s.CurrentPhase, s.PhaseVersion
}
func (s State) GetArrayStatus() arraystatus.ArrayStatus {
	return s.ArrayStatus
}
func (s *State) GetOriginalArraySize() int64 {
	return s.OriginalArraySize
}
func (s *State) GetOriginalMinSuccesses() int64 {
	return s.OriginalMinSuccesses
}
func (s *State) GetIndexesToCache() *bitarray.BitSet {
	return s.IndexesToCache
}
func (s *State) GetExecutionErr() *idlCore.ExecutionError {
	return s.ExecutionErr
}
func (s *State) SetExecutionErr(err *idlCore.ExecutionError) *State {
	s.ExecutionErr = err
	return s
}
func (s *State) SetIndexesToCache(set *bitarray.BitSet) *State {
	s.IndexesToCache = set
	return s
}
func (s *State) SetOriginalArraySize(size int64) *State {
	s.OriginalArraySize = size
	return s
}
func (s *State) SetOriginalMinSuccesses(size int64) *State {
	s.OriginalMinSuccesses = size
	return s
}
func (s *State) SetReason(reason string) *State {
	s.Reason = reason
	return s
}
func (s *State) SetExecutionArraySize(size int) *State {
	s.ExecutionArraySize = size
	return s
}
func (s *State) SetPhase(phase Phase, phaseVersion uint32) *State {
	s.CurrentPhase = phase
	s.PhaseVersion = phaseVersion
	return s
}
func (s *State) SetArrayStatus(state arraystatus.ArrayStatus) *State {
	s.ArrayStatus = state
	return s
}
const (
	ErrorWorkQueue        errors.ErrorCode = "CATALOG_READER_QUEUE_FAILED"
	ErrorInternalMismatch errors.ErrorCode = "ARRAY_MISMATCH"
	ErrorK8sArrayGeneric  errors.ErrorCode = "ARRAY_JOB_GENERIC_FAILURE"
)
func ToArrayJob(structObj *structpb.Struct, taskTypeVersion int32) (*idlPlugins.ArrayJob, error) {
	if structObj == nil {
		if taskTypeVersion == 0 {
			return &idlPlugins.ArrayJob{
				Parallelism: 1,
				Size:        1,
				SuccessCriteria: &idlPlugins.ArrayJob_MinSuccesses{
					MinSuccesses: 1,
				},
			}, nil
		}
		return &idlPlugins.ArrayJob{
			Parallelism: 1,
			Size:        1,
			SuccessCriteria: &idlPlugins.ArrayJob_MinSuccessRatio{
				MinSuccessRatio: 1.0,
			},
		}, nil
	}
	arrayJob := &idlPlugins.ArrayJob{}
	err := utils.UnmarshalStruct(structObj, arrayJob)
	return arrayJob, err
}
func GetPhaseVersionOffset(currentPhase Phase, length int64) uint32 {
	return uint32(length * (int64(core.PhasePermanentFailure) + 1) * int64(currentPhase))
}
func MapArrayStateToPluginPhase(_ context.Context, state *State, logLinks []*idlCore.TaskLog, subTaskIDs []*string) (core.PhaseInfo, error) {
	phaseInfo := core.PhaseInfoUndefined
	t := time.Now()
	nowTaskInfo := &core.TaskInfo{
		OccurredAt: &t,
		Logs:       logLinks,
	}
	if nowTaskInfo.Metadata == nil {
		nowTaskInfo.Metadata = &event.TaskExecutionMetadata{}
	}
	for _, subTaskID := range subTaskIDs {
		nowTaskInfo.Metadata.ExternalResources = append(nowTaskInfo.Metadata.ExternalResources, &event.ExternalResourceInfo{
			ExternalId: *subTaskID,
		})
	}
	switch p, version := state.GetPhase(); p {
	case PhaseStart:
		phaseInfo = core.PhaseInfoInitializing(t, core.DefaultPhaseVersion, state.GetReason(), &core.TaskInfo{OccurredAt: &t})
	case PhasePreLaunch:
		version := GetPhaseVersionOffset(p, 1) + version
		phaseInfo = core.PhaseInfoRunning(version, nowTaskInfo)
	case PhaseLaunch:
		phaseInfo = core.PhaseInfoRunning(version, nowTaskInfo)
	case PhaseWaitingForResources:
		phaseInfo = core.PhaseInfoWaitingForResourcesInfo(t, version, state.GetReason(), nowTaskInfo)
	case PhaseCheckingSubTaskExecutions:
		fallthrough
	case PhaseAssembleFinalOutput:
		fallthrough
	case PhaseAssembleFinalError:
		fallthrough
	case PhaseWriteToDiscoveryThenFail:
		fallthrough
	case PhaseWriteToDiscovery:
		version := GetPhaseVersionOffset(p, state.GetOriginalArraySize()) + version
		phaseInfo = core.PhaseInfoRunning(version, nowTaskInfo)
	case PhaseSuccess:
		phaseInfo = core.PhaseInfoSuccess(nowTaskInfo)
	case PhaseRetryableFailure:
		if state.GetExecutionErr() != nil {
			phaseInfo = core.PhaseInfoFailed(core.PhaseRetryableFailure, state.GetExecutionErr(), nowTaskInfo)
		} else {
			phaseInfo = core.PhaseInfoRetryableFailure(ErrorK8sArrayGeneric, state.GetReason(), nowTaskInfo)
		}
	case PhasePermanentFailure:
		if state.GetExecutionErr() != nil {
			phaseInfo = core.PhaseInfoFailed(core.PhasePermanentFailure, state.GetExecutionErr(), nowTaskInfo)
		} else {
			phaseInfo = core.PhaseInfoSystemFailure(ErrorK8sArrayGeneric, state.GetReason(), nowTaskInfo)
		}
	default:
		return phaseInfo, fmt.Errorf("failed to map custom state phase to core phase. State Phase [%v]", p)
	}
	return phaseInfo, nil
}
func SummaryToPhase(ctx context.Context, minSuccesses int64, summary arraystatus.ArraySummary) Phase {
	totalCount := int64(0)
	totalSuccesses := int64(0)
	totalFailures := int64(0)
	totalRunning := int64(0)
	totalWaitingForResources := int64(0)
	for phase, count := range summary {
		totalCount += count
		if phase.IsTerminal() {
			if phase.IsSuccess() {
				totalSuccesses += count
			} else {
				totalFailures += count
			}
		} else if phase.IsWaitingForResources() {
			totalWaitingForResources += count
		} else {
			totalRunning += count
		}
	}
	if totalCount < minSuccesses {
		logger.Infof(ctx, "Array failed because totalCount[%v] < minSuccesses[%v]", totalCount, minSuccesses)
		return PhaseWriteToDiscoveryThenFail
	}
	if totalRunning+totalSuccesses+totalWaitingForResources < minSuccesses {
		logger.Infof(ctx, "Array failed early because total failures > minSuccesses[%v]. Snapshot totalRunning[%v] + totalSuccesses[%v] + totalWaitingForResource[%v]",
			minSuccesses, totalRunning, totalSuccesses, totalWaitingForResources)
		return PhaseWriteToDiscoveryThenFail
	}
	if totalWaitingForResources > 0 {
		logger.Infof(ctx, "Array is still running and waiting for resources totalWaitingForResources[%v]", totalWaitingForResources)
		return PhaseWaitingForResources
	}
	if totalSuccesses >= minSuccesses && totalRunning == 0 {
		logger.Infof(ctx, "Array succeeded because totalSuccesses[%v] >= minSuccesses[%v]", totalSuccesses, minSuccesses)
		return PhaseWriteToDiscovery
	}
	logger.Debugf(ctx, "Array is still running [Successes: %v, Failures: %v, Total: %v, MinSuccesses: %v]",
		totalSuccesses, totalFailures, totalCount, minSuccesses)
	return PhaseCheckingSubTaskExecutions
}
func InvertBitSet(input *bitarray.BitSet, limit uint) *bitarray.BitSet {
	output := bitarray.NewBitSet(limit)
	for i := uint(0); i < limit; i++ {
		if !input.IsSet(i) {
			output.Set(i)
		}
	}
	return output
}
func NewPhasesCompactArray(count uint) bitarray.CompactArray {
	a, err := bitarray.NewCompactArray(count, bitarray.Item(len(core.Phases)-1))
	if err != nil {
		logger.Warnf(context.Background(), "Failed to create compact array with provided parameters [count: %v]",
			count)
		return bitarray.CompactArray{}
	}
	return a
}