diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index ead3d2a09e8..4b1b1c7af1a 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -9754,6 +9754,10 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate." + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" diff --git a/api/openapi-spec/v3/api__v1_openapi.json b/api/openapi-spec/v3/api__v1_openapi.json index 006ed2a67fe..d9e477a3ff9 100644 --- a/api/openapi-spec/v3/api__v1_openapi.json +++ b/api/openapi-spec/v3/api__v1_openapi.json @@ -5681,6 +5681,14 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.ResourceRequirements" + } + ], + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate." + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" diff --git a/api/openapi-spec/v3/apis__apps__v1_openapi.json b/api/openapi-spec/v3/apis__apps__v1_openapi.json index 39b0dbb10ce..9fea2c12b3c 100644 --- a/api/openapi-spec/v3/apis__apps__v1_openapi.json +++ b/api/openapi-spec/v3/apis__apps__v1_openapi.json @@ -3928,6 +3928,14 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.ResourceRequirements" + } + ], + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate." + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" diff --git a/api/openapi-spec/v3/apis__batch__v1_openapi.json b/api/openapi-spec/v3/apis__batch__v1_openapi.json index a1f68962e7e..b7b1f6731a3 100644 --- a/api/openapi-spec/v3/apis__batch__v1_openapi.json +++ b/api/openapi-spec/v3/apis__batch__v1_openapi.json @@ -3130,6 +3130,14 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.ResourceRequirements" + } + ], + "description": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate." + }, "restartPolicy": { "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", "type": "string" diff --git a/pkg/api/pod/testing/make.go b/pkg/api/pod/testing/make.go index b73de5d1a11..71c29614a70 100644 --- a/pkg/api/pod/testing/make.go +++ b/pkg/api/pod/testing/make.go @@ -72,6 +72,12 @@ func SetResourceVersion(rv string) Tweak { } } +func SetPodResources(resources *api.ResourceRequirements) Tweak { + return func(pod *api.Pod) { + pod.Spec.Resources = resources + } +} + func SetContainers(containers ...api.Container) Tweak { return func(pod *api.Pod) { pod.Spec.Containers = containers diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index a8e19064838..36f2e869f9f 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -384,6 +384,7 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po AllowNamespacedSysctlsForHostNetAndHostIPC: false, AllowNonLocalProjectedTokenPath: false, AllowPodLifecycleSleepActionZeroValue: utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepActionAllowZero), + PodLevelResourcesEnabled: utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources), } // If old spec uses relaxed validation or enabled the RelaxedEnvironmentVariableValidation feature gate, @@ -621,6 +622,7 @@ func dropDisabledFields( } } + dropDisabledPodLevelResources(podSpec, oldPodSpec) dropDisabledProcMountField(podSpec, oldPodSpec) dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec) @@ -674,6 +676,14 @@ func dropDisabledFields( dropSELinuxChangePolicy(podSpec, oldPodSpec) } +func dropDisabledPodLevelResources(podSpec, oldPodSpec *api.PodSpec) { + // If the feature is disabled and not in use, drop Resources at the pod-level + // from PodSpec. + if !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) && !podLevelResourcesInUse(oldPodSpec) { + podSpec.Resources = nil + } +} + func dropPodLifecycleSleepAction(podSpec, oldPodSpec *api.PodSpec) { if utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepAction) || podLifecycleSleepActionInUse(oldPodSpec) { return @@ -1050,6 +1060,28 @@ func supplementalGroupsPolicyInUse(podSpec *api.PodSpec) bool { return false } +// podLevelResourcesInUse returns true if pod-spec is non-nil and Resources field at +// pod-level has non-empty Requests or Limits. +func podLevelResourcesInUse(podSpec *api.PodSpec) bool { + if podSpec == nil { + return false + } + + if podSpec.Resources == nil { + return false + } + + if len(podSpec.Resources.Requests) > 0 { + return true + } + + if len(podSpec.Resources.Limits) > 0 { + return true + } + + return false +} + // inPlacePodVerticalScalingInUse returns true if pod spec is non-nil and ResizePolicy is set func inPlacePodVerticalScalingInUse(podSpec *api.PodSpec) bool { if podSpec == nil { diff --git a/pkg/api/pod/util_test.go b/pkg/api/pod/util_test.go index 67ab805b7a6..2f2f58e3f81 100644 --- a/pkg/api/pod/util_test.go +++ b/pkg/api/pod/util_test.go @@ -2703,6 +2703,149 @@ func TestDropInPlacePodVerticalScaling(t *testing.T) { } } +func TestDropPodLevelResources(t *testing.T) { + containers := []api.Container{ + { + Name: "c1", + Image: "image", + Resources: api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")}, + }, + }, + } + podWithPodLevelResources := func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{ + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("50Gi"), + }, + Limits: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("50Gi"), + }, + }, + Containers: containers, + }, + } + } + + podWithoutPodLevelResources := func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{ + Containers: containers, + }, + } + } + + podInfo := []struct { + description string + hasPodLevelResources bool + pod func() *api.Pod + }{ + { + description: "has pod-level resources", + hasPodLevelResources: true, + pod: podWithPodLevelResources, + }, + { + description: "does not have pod-level resources", + hasPodLevelResources: false, + pod: podWithoutPodLevelResources, + }, + { + description: "is nil", + hasPodLevelResources: false, + pod: func() *api.Pod { return nil }, + }, + { + description: "is empty struct", + hasPodLevelResources: false, + // refactor to generalize and use podWithPodLevelResources() + pod: func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{ + Resources: &api.ResourceRequirements{}, + Containers: containers, + }, + } + }, + }, + { + description: "is empty Requests list", + hasPodLevelResources: false, + pod: func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{}, + }}} + }, + }, + { + description: "is empty Limits list", + hasPodLevelResources: false, + pod: func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{Resources: &api.ResourceRequirements{ + Limits: api.ResourceList{}, + }}} + }, + }, + } + + for _, enabled := range []bool{true, false} { + for _, oldPodInfo := range podInfo { + for _, newPodInfo := range podInfo { + oldPodHasPodLevelResources, oldPod := oldPodInfo.hasPodLevelResources, oldPodInfo.pod() + newPodHasPodLevelResources, newPod := newPodInfo.hasPodLevelResources, newPodInfo.pod() + if newPod == nil { + continue + } + + t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, enabled) + + var oldPodSpec *api.PodSpec + if oldPod != nil { + oldPodSpec = &oldPod.Spec + } + + dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil) + + // old pod should never be changed + if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) { + t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod())) + } + + switch { + case enabled || oldPodHasPodLevelResources: + // new pod shouldn't change if feature enabled or if old pod has + // any pod level resources + if !reflect.DeepEqual(newPod, newPodInfo.pod()) { + t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod())) + } + case newPodHasPodLevelResources: + // new pod should be changed + if reflect.DeepEqual(newPod, newPodInfo.pod()) { + t.Errorf("new pod was not changed") + } + // new pod should not have any pod-level resources + if !reflect.DeepEqual(newPod, podWithoutPodLevelResources()) { + t.Errorf("new pod has pod-level resources: %v", cmp.Diff(newPod, podWithoutPodLevelResources())) + } + default: + if newPod.Spec.Resources != nil { + t.Errorf("expected nil, got: %v", newPod.Spec.Resources) + } + } + }) + } + } + } +} + func TestDropSidecarContainers(t *testing.T) { containerRestartPolicyAlways := api.ContainerRestartPolicyAlways diff --git a/pkg/apis/apps/v1/zz_generated.defaults.go b/pkg/apis/apps/v1/zz_generated.defaults.go index ef249608d90..99bcf2c7ee5 100644 --- a/pkg/apis/apps/v1/zz_generated.defaults.go +++ b/pkg/apis/apps/v1/zz_generated.defaults.go @@ -339,6 +339,10 @@ func SetObjectDefaults_DaemonSet(in *appsv1.DaemonSet) { } } apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DaemonSetList(in *appsv1.DaemonSetList) { @@ -644,6 +648,10 @@ func SetObjectDefaults_Deployment(in *appsv1.Deployment) { } } apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DeploymentList(in *appsv1.DeploymentList) { @@ -949,6 +957,10 @@ func SetObjectDefaults_ReplicaSet(in *appsv1.ReplicaSet) { } } apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_ReplicaSetList(in *appsv1.ReplicaSetList) { @@ -1254,6 +1266,10 @@ func SetObjectDefaults_StatefulSet(in *appsv1.StatefulSet) { } } apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } for i := range in.Spec.VolumeClaimTemplates { a := &in.Spec.VolumeClaimTemplates[i] apiscorev1.SetDefaults_PersistentVolumeClaim(a) diff --git a/pkg/apis/apps/v1beta1/zz_generated.defaults.go b/pkg/apis/apps/v1beta1/zz_generated.defaults.go index 648ba507bc4..3b54c802a34 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.defaults.go +++ b/pkg/apis/apps/v1beta1/zz_generated.defaults.go @@ -335,6 +335,10 @@ func SetObjectDefaults_Deployment(in *appsv1beta1.Deployment) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DeploymentList(in *appsv1beta1.DeploymentList) { @@ -640,6 +644,10 @@ func SetObjectDefaults_StatefulSet(in *appsv1beta1.StatefulSet) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } for i := range in.Spec.VolumeClaimTemplates { a := &in.Spec.VolumeClaimTemplates[i] corev1.SetDefaults_PersistentVolumeClaim(a) diff --git a/pkg/apis/apps/v1beta2/zz_generated.defaults.go b/pkg/apis/apps/v1beta2/zz_generated.defaults.go index a1b414ce9b6..35aacff8624 100644 --- a/pkg/apis/apps/v1beta2/zz_generated.defaults.go +++ b/pkg/apis/apps/v1beta2/zz_generated.defaults.go @@ -339,6 +339,10 @@ func SetObjectDefaults_DaemonSet(in *appsv1beta2.DaemonSet) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DaemonSetList(in *appsv1beta2.DaemonSetList) { @@ -644,6 +648,10 @@ func SetObjectDefaults_Deployment(in *appsv1beta2.Deployment) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DeploymentList(in *appsv1beta2.DeploymentList) { @@ -949,6 +957,10 @@ func SetObjectDefaults_ReplicaSet(in *appsv1beta2.ReplicaSet) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_ReplicaSetList(in *appsv1beta2.ReplicaSetList) { @@ -1254,6 +1266,10 @@ func SetObjectDefaults_StatefulSet(in *appsv1beta2.StatefulSet) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } for i := range in.Spec.VolumeClaimTemplates { a := &in.Spec.VolumeClaimTemplates[i] corev1.SetDefaults_PersistentVolumeClaim(a) diff --git a/pkg/apis/batch/v1/zz_generated.defaults.go b/pkg/apis/batch/v1/zz_generated.defaults.go index 70a1b5fef7c..eb6ce80a1f9 100644 --- a/pkg/apis/batch/v1/zz_generated.defaults.go +++ b/pkg/apis/batch/v1/zz_generated.defaults.go @@ -335,6 +335,10 @@ func SetObjectDefaults_CronJob(in *batchv1.CronJob) { } } apiscorev1.SetDefaults_ResourceList(&in.Spec.JobTemplate.Spec.Template.Spec.Overhead) + if in.Spec.JobTemplate.Spec.Template.Spec.Resources != nil { + apiscorev1.SetDefaults_ResourceList(&in.Spec.JobTemplate.Spec.Template.Spec.Resources.Limits) + apiscorev1.SetDefaults_ResourceList(&in.Spec.JobTemplate.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_CronJobList(in *batchv1.CronJobList) { @@ -640,6 +644,10 @@ func SetObjectDefaults_Job(in *batchv1.Job) { } } apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + apiscorev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_JobList(in *batchv1.JobList) { diff --git a/pkg/apis/batch/v1beta1/zz_generated.defaults.go b/pkg/apis/batch/v1beta1/zz_generated.defaults.go index ba79028e710..7e517afd0c1 100644 --- a/pkg/apis/batch/v1beta1/zz_generated.defaults.go +++ b/pkg/apis/batch/v1beta1/zz_generated.defaults.go @@ -333,6 +333,10 @@ func SetObjectDefaults_CronJob(in *batchv1beta1.CronJob) { } } corev1.SetDefaults_ResourceList(&in.Spec.JobTemplate.Spec.Template.Spec.Overhead) + if in.Spec.JobTemplate.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.JobTemplate.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.JobTemplate.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_CronJobList(in *batchv1beta1.CronJobList) { diff --git a/pkg/apis/core/helper/qos/qos.go b/pkg/apis/core/helper/qos/qos.go index b32fffa0e3f..4f81d646b22 100644 --- a/pkg/apis/core/helper/qos/qos.go +++ b/pkg/apis/core/helper/qos/qos.go @@ -21,7 +21,9 @@ package qos import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/sets" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/features" ) var supportedQoSComputeResources = sets.NewString(string(core.ResourceCPU), string(core.ResourceMemory)) @@ -39,6 +41,45 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass { return ComputePodQOS(pod) } +// zeroQuantity represents a resource.Quantity with value "0", used as a baseline +// for resource comparisons. +var zeroQuantity = resource.MustParse("0") + +// processResourceList adds non-zero quantities for supported QoS compute resources +// quantities from newList to list. +func processResourceList(list, newList core.ResourceList) { + for name, quantity := range newList { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + delta := quantity.DeepCopy() + if _, exists := list[name]; !exists { + list[name] = delta + } else { + delta.Add(list[name]) + list[name] = delta + } + } + } +} + +// getQOSResources returns a set of resource names from the provided resource list that: +// 1. Are supported QoS compute resources +// 2. Have quantities greater than zero +func getQOSResources(list core.ResourceList) sets.Set[string] { + qosResources := sets.New[string]() + for name, quantity := range list { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + qosResources.Insert(string(name)) + } + } + return qosResources +} + // ComputePodQOS evaluates the list of containers to determine a pod's QoS class. This function is more // expensive than GetPodQOS which should be used for pods having a non-empty .Status.QOSClass. // A pod is besteffort if none of its containers have specified any requests or limits. @@ -48,54 +89,72 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass { func ComputePodQOS(pod *core.Pod) core.PodQOSClass { requests := core.ResourceList{} limits := core.ResourceList{} - zeroQuantity := resource.MustParse("0") isGuaranteed := true - // note, ephemeral containers are not considered for QoS as they cannot define resources - allContainers := []core.Container{} - allContainers = append(allContainers, pod.Spec.Containers...) - allContainers = append(allContainers, pod.Spec.InitContainers...) - for _, container := range allContainers { - // process requests - for name, quantity := range container.Resources.Requests { - if !isSupportedQoSComputeResource(name) { - continue - } - if quantity.Cmp(zeroQuantity) == 1 { - delta := quantity.DeepCopy() - if _, exists := requests[name]; !exists { - requests[name] = delta - } else { - delta.Add(requests[name]) - requests[name] = delta - } - } - } - // process limits - qosLimitsFound := sets.NewString() - for name, quantity := range container.Resources.Limits { - if !isSupportedQoSComputeResource(name) { - continue - } - if quantity.Cmp(zeroQuantity) == 1 { - qosLimitsFound.Insert(string(name)) - delta := quantity.DeepCopy() - if _, exists := limits[name]; !exists { - limits[name] = delta - } else { - delta.Add(limits[name]) - limits[name] = delta - } - } + // When pod-level resources are specified, we use them to determine QoS class. + if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) && + pod.Spec.Resources != nil { + if len(pod.Spec.Resources.Requests) > 0 { + // process requests + processResourceList(requests, pod.Spec.Resources.Requests) } - if !qosLimitsFound.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { - isGuaranteed = false + if len(pod.Spec.Resources.Limits) > 0 { + // process limits + processResourceList(limits, pod.Spec.Resources.Limits) + qosLimitResources := getQOSResources(pod.Spec.Resources.Limits) + if !qosLimitResources.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { + isGuaranteed = false + } + } + } else { + // note, ephemeral containers are not considered for QoS as they cannot define resources + allContainers := []core.Container{} + allContainers = append(allContainers, pod.Spec.Containers...) + allContainers = append(allContainers, pod.Spec.InitContainers...) + for _, container := range allContainers { + // process requests + for name, quantity := range container.Resources.Requests { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + delta := quantity.DeepCopy() + if _, exists := requests[name]; !exists { + requests[name] = delta + } else { + delta.Add(requests[name]) + requests[name] = delta + } + } + } + // process limits + qosLimitsFound := sets.NewString() + for name, quantity := range container.Resources.Limits { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + qosLimitsFound.Insert(string(name)) + delta := quantity.DeepCopy() + if _, exists := limits[name]; !exists { + limits[name] = delta + } else { + delta.Add(limits[name]) + limits[name] = delta + } + } + } + + if !qosLimitsFound.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { + isGuaranteed = false + } } } + if len(requests) == 0 && len(limits) == 0 { return core.PodQOSBestEffort } - // Check is requests match limits for all resources. + // Check if requests match limits for all resources. if isGuaranteed { for name, req := range requests { if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 { diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go index 8bc34a99ef6..664eb4897a5 100644 --- a/pkg/apis/core/types.go +++ b/pkg/apis/core/types.go @@ -3609,6 +3609,20 @@ type PodSpec struct { // +featureGate=DynamicResourceAllocation // +optional ResourceClaims []PodResourceClaim + // Resources is the total amount of CPU and Memory resources required by all + // containers in the pod. It supports specifying Requests and Limits for + // "cpu" and "memory" resource names only. ResourceClaims are not supported. + // + // This field enables fine-grained control over resource allocation for the + // entire pod, allowing resource sharing among containers in a pod. + // TODO: For beta graduation, expand this comment with a detailed explanation. + // + // This is an alpha field and requires enabling the PodLevelResources feature + // gate. + // + // +featureGate=PodLevelResources + // +optional + Resources *ResourceRequirements } // PodResourceClaim references exactly one ResourceClaim through a ClaimSource. diff --git a/pkg/apis/core/v1/defaults.go b/pkg/apis/core/v1/defaults.go index aa5f448f791..a058c5f7c7f 100644 --- a/pkg/apis/core/v1/defaults.go +++ b/pkg/apis/core/v1/defaults.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" utilfeature "k8s.io/apiserver/pkg/util/feature" + resourcehelper "k8s.io/component-helpers/resource" "k8s.io/kubernetes/pkg/api/v1/service" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/parsers" @@ -217,6 +218,13 @@ func SetDefaults_Pod(obj *v1.Pod) { } } } + + // Pod Requests default values must be applied after container-level default values + // have been populated. + if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) { + defaultPodRequests(obj) + } + if obj.Spec.EnableServiceLinks == nil { enableServiceLinks := v1.DefaultEnableServiceLinks obj.Spec.EnableServiceLinks = &enableServiceLinks @@ -438,3 +446,56 @@ func SetDefaults_PodLogOptions(obj *v1.PodLogOptions) { } } } + +// defaultPodRequests applies default values for pod-level requests, only when +// pod-level limits are set, in following scenarios: +// 1. When at least one container (regular, init or sidecar) has requests set: +// The pod-level requests become equal to the effective requests of all containers +// in the pod. +// 2. When no containers have requests set: The pod-level requests become equal to +// pod-level limits. +// This defaulting behavior ensures consistent resource accounting at the pod-level +// while maintaining compatibility with the container-level specifications, as detailed +// in KEP-2837: https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/2837-pod-level-resource-spec/README.md#proposed-validation--defaulting-rules +func defaultPodRequests(obj *v1.Pod) { + // We only populate defaults when the pod-level resources are partly specified already. + if obj.Spec.Resources == nil { + return + } + + if len(obj.Spec.Resources.Limits) == 0 { + return + } + + var podReqs v1.ResourceList + podReqs = obj.Spec.Resources.Requests + if podReqs == nil { + podReqs = make(v1.ResourceList) + } + + aggrCtrReqs := resourcehelper.AggregateContainerRequests(obj, resourcehelper.PodResourcesOptions{}) + + // When containers specify requests for a resource (supported by + // PodLevelResources feature) and pod-level requests are not set, the pod-level requests + // default to the effective requests of all the containers for that resource. + for key, aggrCtrLim := range aggrCtrReqs { + if _, exists := podReqs[key]; !exists && resourcehelper.IsSupportedPodLevelResource(key) { + podReqs[key] = aggrCtrLim.DeepCopy() + } + } + + // When no containers specify requests for a resource, the pod-level requests + // will default to match the pod-level limits, if pod-level + // limits exist for that resource. + for key, podLim := range obj.Spec.Resources.Limits { + if _, exists := podReqs[key]; !exists && resourcehelper.IsSupportedPodLevelResource(key) { + podReqs[key] = podLim.DeepCopy() + } + } + + // Only set pod-level resource requests in the PodSpec if the requirements map + // contains entries after collecting container-level requests and pod-level limits. + if len(podReqs) > 0 { + obj.Spec.Resources.Requests = podReqs + } +} diff --git a/pkg/apis/core/v1/defaults_test.go b/pkg/apis/core/v1/defaults_test.go index d8292d85262..a460a274b07 100644 --- a/pkg/apis/core/v1/defaults_test.go +++ b/pkg/apis/core/v1/defaults_test.go @@ -380,6 +380,880 @@ func testPodDefaults(t *testing.T, featuresEnabled bool) { } } +func TestPodResourcesDefaults(t *testing.T) { + cases := []struct { + name string + podLevelResourcesEnabled bool + containers []v1.Container + podResources *v1.ResourceRequirements + expectedPodSpec v1.PodSpec + }{ + { + name: "pod resources=unset, container resources=unset", + containers: []v1.Container{{Resources: v1.ResourceRequirements{}}, {Resources: v1.ResourceRequirements{}}}, + expectedPodSpec: v1.PodSpec{ + Containers: []v1.Container{{Resources: v1.ResourceRequirements{}}, {Resources: v1.ResourceRequirements{}}}, + }, + }, { + name: "pod resources=unset, container requests=unset limits=set", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod resources=unset, container requests=set limits=unset", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod resources=unset, container requests=set limits=set", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=unset limits=set, container resources=unset", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, + }, { + name: "pod limits=nil, container requests=unset limits=set", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: nil, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod limits=empty map, container requests=unset limits=set", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{}, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=empty map limits=set, container requests=unset limits=set", + podLevelResourcesEnabled: true, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{}, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("5m"), + "memory": resource.MustParse("7Mi"), + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("3m"), + "memory": resource.MustParse("6Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("5m"), + "memory": resource.MustParse("7Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=nil limits=set, container requests=unset limits=set", + podLevelResourcesEnabled: true, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: nil, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("5m"), + "memory": resource.MustParse("7Mi"), + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("3m"), + "memory": resource.MustParse("6Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("5m"), + "memory": resource.MustParse("7Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=unset limits=set, container requests=unset limits=set", + podLevelResourcesEnabled: true, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("5m"), + "memory": resource.MustParse("7Mi"), + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("3m"), + "memory": resource.MustParse("6Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("5m"), + "memory": resource.MustParse("7Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=unset limits=set, container requests=set limits=unset", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("8Mi"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("2Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("3Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("8Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("2Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=unset cpu limits=set, container resources=unset", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{}, + }, { + Resources: v1.ResourceRequirements{}, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{}, + }, { + Resources: v1.ResourceRequirements{}, + }, + }, + }, + }, { + name: "pod requests=unset limits=set, container requests=set limits=set", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("8Mi"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("2Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("3Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("8Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("2Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod requests=unset limits=set, container memory requests=set limits=unset", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("8Mi"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("6Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("4m"), + "memory": resource.MustParse("8Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("1m"), + "memory": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod limits=set, container unsupported requests=set limits=set", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod unsupported resources limits=set, container unsupported requests=set limits=set", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{}, + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }, { + name: "pod supported and unsupported resources limits=set, container unsupported requests=set limits=set", + podLevelResourcesEnabled: true, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + "storage": resource.MustParse("1Mi"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + }, + }, + }, + expectedPodSpec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "cpu": resource.MustParse("2m"), + "memory": resource.MustParse("1Mi"), + }, + }, + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + Limits: v1.ResourceList{ + "storage": resource.MustParse("1Mi"), + }, + }, + }, { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + Limits: v1.ResourceList{ + "ephemeral-storage": resource.MustParse("5Mi"), + }, + }, + }, + }, + }, + }} + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.podLevelResourcesEnabled { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, true) + } + spec := v1.PodSpec{ + Containers: tc.containers, + Resources: tc.podResources, + } + p := v1.Pod{Spec: *spec.DeepCopy()} + corev1.SetDefaults_Pod(&p) + for i, container := range p.Spec.Containers { + for resource, quantity := range container.Resources.Requests { + if quantity.Cmp(tc.expectedPodSpec.Containers[i].Resources.Requests[resource]) != 0 { + t.Errorf("got: %v, expected: %v", quantity, tc.expectedPodSpec.Containers[i].Resources.Requests[resource]) + } + } + } + + if tc.podResources != nil { + for resource, quantity := range p.Spec.Resources.Requests { + if quantity.Cmp(tc.expectedPodSpec.Resources.Requests[resource]) != 0 { + t.Errorf("got: %v, expected: %v", quantity, tc.expectedPodSpec.Resources.Requests[resource]) + } + } + } + }) + } +} + func TestPodHostNetworkDefaults(t *testing.T) { cases := []struct { name string diff --git a/pkg/apis/core/v1/helper/qos/qos.go b/pkg/apis/core/v1/helper/qos/qos.go index 79e2eb2abd2..b6ba7bf63ae 100644 --- a/pkg/apis/core/v1/helper/qos/qos.go +++ b/pkg/apis/core/v1/helper/qos/qos.go @@ -20,7 +20,9 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/sets" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/features" ) var supportedQoSComputeResources = sets.NewString(string(core.ResourceCPU), string(core.ResourceMemory)) @@ -41,57 +43,117 @@ func GetPodQOS(pod *v1.Pod) v1.PodQOSClass { return ComputePodQOS(pod) } +// zeroQuantity represents a resource.Quantity with value "0", used as a baseline +// for resource comparisons. +var zeroQuantity = resource.MustParse("0") + +// processResourceList adds non-zero quantities for supported QoS compute resources +// quantities from newList to list. +func processResourceList(list, newList v1.ResourceList) { + for name, quantity := range newList { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + delta := quantity.DeepCopy() + if _, exists := list[name]; !exists { + list[name] = delta + } else { + delta.Add(list[name]) + list[name] = delta + } + } + } +} + +// getQOSResources returns a set of resource names from the provided resource list that: +// 1. Are supported QoS compute resources +// 2. Have quantities greater than zero +func getQOSResources(list v1.ResourceList) sets.Set[string] { + qosResources := sets.New[string]() + for name, quantity := range list { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + qosResources.Insert(string(name)) + } + } + return qosResources +} + // ComputePodQOS evaluates the list of containers to determine a pod's QoS class. This function is more // expensive than GetPodQOS which should be used for pods having a non-empty .Status.QOSClass. // A pod is besteffort if none of its containers have specified any requests or limits. // A pod is guaranteed only when requests and limits are specified for all the containers and they are equal. // A pod is burstable if limits and requests do not match across all containers. +// TODO(ndixita): Refactor ComputePodQOS into smaller functions to make it more +// readable and maintainable. func ComputePodQOS(pod *v1.Pod) v1.PodQOSClass { requests := v1.ResourceList{} limits := v1.ResourceList{} - zeroQuantity := resource.MustParse("0") isGuaranteed := true - allContainers := []v1.Container{} - allContainers = append(allContainers, pod.Spec.Containers...) - allContainers = append(allContainers, pod.Spec.InitContainers...) - for _, container := range allContainers { - // process requests - for name, quantity := range container.Resources.Requests { - if !isSupportedQoSComputeResource(name) { - continue - } - if quantity.Cmp(zeroQuantity) == 1 { - delta := quantity.DeepCopy() - if _, exists := requests[name]; !exists { - requests[name] = delta - } else { - delta.Add(requests[name]) - requests[name] = delta - } - } - } - // process limits - qosLimitsFound := sets.NewString() - for name, quantity := range container.Resources.Limits { - if !isSupportedQoSComputeResource(name) { - continue - } - if quantity.Cmp(zeroQuantity) == 1 { - qosLimitsFound.Insert(string(name)) - delta := quantity.DeepCopy() - if _, exists := limits[name]; !exists { - limits[name] = delta - } else { - delta.Add(limits[name]) - limits[name] = delta - } - } + // When pod-level resources are specified, we use them to determine QoS class. + if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) && + pod.Spec.Resources != nil { + if len(pod.Spec.Resources.Requests) > 0 { + // process requests + processResourceList(requests, pod.Spec.Resources.Requests) } - if !qosLimitsFound.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) { - isGuaranteed = false + if len(pod.Spec.Resources.Limits) > 0 { + // process limits + processResourceList(limits, pod.Spec.Resources.Limits) + qosLimitResources := getQOSResources(pod.Spec.Resources.Limits) + if !qosLimitResources.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) { + isGuaranteed = false + } + } + } else { + // note, ephemeral containers are not considered for QoS as they cannot define resources + allContainers := []v1.Container{} + allContainers = append(allContainers, pod.Spec.Containers...) + allContainers = append(allContainers, pod.Spec.InitContainers...) + for _, container := range allContainers { + // process requests + for name, quantity := range container.Resources.Requests { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + delta := quantity.DeepCopy() + if _, exists := requests[name]; !exists { + requests[name] = delta + } else { + delta.Add(requests[name]) + requests[name] = delta + } + } + } + // process limits + qosLimitsFound := sets.NewString() + for name, quantity := range container.Resources.Limits { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + qosLimitsFound.Insert(string(name)) + delta := quantity.DeepCopy() + if _, exists := limits[name]; !exists { + limits[name] = delta + } else { + delta.Add(limits[name]) + limits[name] = delta + } + } + } + + if !qosLimitsFound.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) { + isGuaranteed = false + } } } + if len(requests) == 0 && len(limits) == 0 { return v1.PodQOSBestEffort } diff --git a/pkg/apis/core/v1/helper/qos/qos_test.go b/pkg/apis/core/v1/helper/qos/qos_test.go index d16c17a14e7..29b9302c9cf 100644 --- a/pkg/apis/core/v1/helper/qos/qos_test.go +++ b/pkg/apis/core/v1/helper/qos/qos_test.go @@ -22,15 +22,19 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/core/helper/qos" corev1 "k8s.io/kubernetes/pkg/apis/core/v1" + "k8s.io/kubernetes/pkg/features" ) func TestComputePodQOS(t *testing.T) { testCases := []struct { - pod *v1.Pod - expected v1.PodQOSClass + pod *v1.Pod + expected v1.PodQOSClass + podLevelResourcesEnabled bool }{ { pod: newPod("guaranteed", []v1.Container{ @@ -126,8 +130,76 @@ func TestComputePodQOS(t *testing.T) { }), expected: v1.PodQOSBurstable, }, + { + pod: newPodWithResources( + "guaranteed-with-pod-level-resources", + []v1.Container{ + newContainer("best-effort", getResourceList("", ""), getResourceList("", "")), + }, + getResourceRequirements(getResourceList("10m", "100Mi"), getResourceList("10m", "100Mi")), + ), + expected: v1.PodQOSGuaranteed, + podLevelResourcesEnabled: true, + }, + { + pod: newPodWithResources( + "guaranteed-with-pod-and-container-level-resources", + []v1.Container{ + newContainer("burstable", getResourceList("3m", "10Mi"), getResourceList("5m", "20Mi")), + }, + getResourceRequirements(getResourceList("10m", "100Mi"), getResourceList("10m", "100Mi")), + ), + expected: v1.PodQOSGuaranteed, + podLevelResourcesEnabled: true, + }, + { + pod: newPodWithResources( + "burstable-with-pod-level-resources", + []v1.Container{ + newContainer("best-effort", getResourceList("", ""), getResourceList("", "")), + }, + getResourceRequirements(getResourceList("10m", "10Mi"), getResourceList("20m", "50Mi")), + ), + expected: v1.PodQOSBurstable, + podLevelResourcesEnabled: true, + }, + { + pod: newPodWithResources( + "burstable-with-pod-and-container-level-resources", + []v1.Container{ + newContainer("burstable", getResourceList("5m", "10Mi"), getResourceList("5m", "10Mi")), + }, + getResourceRequirements(getResourceList("10m", "10Mi"), getResourceList("20m", "50Mi")), + ), + expected: v1.PodQOSBurstable, + podLevelResourcesEnabled: true, + }, + { + pod: newPodWithResources( + "burstable-with-pod-and-container-level-requests", + []v1.Container{ + newContainer("burstable", getResourceList("5m", "10Mi"), getResourceList("", "")), + }, + getResourceRequirements(getResourceList("10m", "10Mi"), getResourceList("", "")), + ), + expected: v1.PodQOSBurstable, + podLevelResourcesEnabled: true, + }, + { + pod: newPodWithResources( + "burstable-with-pod-and-container-level-resources-2", + []v1.Container{ + newContainer("burstable", getResourceList("5m", "10Mi"), getResourceList("", "")), + newContainer("guaranteed", getResourceList("5m", "10Mi"), getResourceList("5m", "10Mi")), + }, + getResourceRequirements(getResourceList("10m", "10Mi"), getResourceList("5m", "")), + ), + expected: v1.PodQOSBurstable, + podLevelResourcesEnabled: true, + }, } for id, testCase := range testCases { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, testCase.podLevelResourcesEnabled) if actual := ComputePodQOS(testCase.pod); testCase.expected != actual { t.Errorf("[%d]: invalid qos pod %s, expected: %s, actual: %s", id, testCase.pod.Name, testCase.expected, actual) } @@ -158,17 +230,17 @@ func addResource(rName, value string, rl v1.ResourceList) v1.ResourceList { return rl } -func getResourceRequirements(requests, limits v1.ResourceList) v1.ResourceRequirements { +func getResourceRequirements(requests, limits v1.ResourceList) *v1.ResourceRequirements { res := v1.ResourceRequirements{} res.Requests = requests res.Limits = limits - return res + return &res } func newContainer(name string, requests v1.ResourceList, limits v1.ResourceList) v1.Container { return v1.Container{ Name: name, - Resources: getResourceRequirements(requests, limits), + Resources: *(getResourceRequirements(requests, limits)), } } @@ -183,6 +255,14 @@ func newPod(name string, containers []v1.Container) *v1.Pod { } } +func newPodWithResources(name string, containers []v1.Container, podResources *v1.ResourceRequirements) *v1.Pod { + pod := newPod(name, containers) + if podResources != nil { + pod.Spec.Resources = podResources + } + return pod +} + func newPodWithInitContainers(name string, containers []v1.Container, initContainers []v1.Container) *v1.Pod { return &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/apis/core/v1/zz_generated.conversion.go b/pkg/apis/core/v1/zz_generated.conversion.go index 9c827442098..6a842771a11 100644 --- a/pkg/apis/core/v1/zz_generated.conversion.go +++ b/pkg/apis/core/v1/zz_generated.conversion.go @@ -6750,6 +6750,7 @@ func autoConvert_v1_PodSpec_To_core_PodSpec(in *corev1.PodSpec, out *core.PodSpe // INFO: in.HostUsers opted out of conversion generation out.SchedulingGates = *(*[]core.PodSchedulingGate)(unsafe.Pointer(&in.SchedulingGates)) out.ResourceClaims = *(*[]core.PodResourceClaim)(unsafe.Pointer(&in.ResourceClaims)) + out.Resources = (*core.ResourceRequirements)(unsafe.Pointer(in.Resources)) return nil } @@ -6805,6 +6806,7 @@ func autoConvert_core_PodSpec_To_v1_PodSpec(in *core.PodSpec, out *corev1.PodSpe out.OS = (*corev1.PodOS)(unsafe.Pointer(in.OS)) out.SchedulingGates = *(*[]corev1.PodSchedulingGate)(unsafe.Pointer(&in.SchedulingGates)) out.ResourceClaims = *(*[]corev1.PodResourceClaim)(unsafe.Pointer(&in.ResourceClaims)) + out.Resources = (*corev1.ResourceRequirements)(unsafe.Pointer(in.Resources)) return nil } diff --git a/pkg/apis/core/v1/zz_generated.defaults.go b/pkg/apis/core/v1/zz_generated.defaults.go index 44d332981c0..3b6eb4f0a93 100644 --- a/pkg/apis/core/v1/zz_generated.defaults.go +++ b/pkg/apis/core/v1/zz_generated.defaults.go @@ -500,6 +500,10 @@ func SetObjectDefaults_Pod(in *corev1.Pod) { } } SetDefaults_ResourceList(&in.Spec.Overhead) + if in.Spec.Resources != nil { + SetDefaults_ResourceList(&in.Spec.Resources.Limits) + SetDefaults_ResourceList(&in.Spec.Resources.Requests) + } for i := range in.Status.InitContainerStatuses { a := &in.Status.InitContainerStatuses[i] SetDefaults_ResourceList(&a.AllocatedResources) @@ -859,6 +863,10 @@ func SetObjectDefaults_PodTemplate(in *corev1.PodTemplate) { } } SetDefaults_ResourceList(&in.Template.Spec.Overhead) + if in.Template.Spec.Resources != nil { + SetDefaults_ResourceList(&in.Template.Spec.Resources.Limits) + SetDefaults_ResourceList(&in.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_PodTemplateList(in *corev1.PodTemplateList) { @@ -1165,6 +1173,10 @@ func SetObjectDefaults_ReplicationController(in *corev1.ReplicationController) { } } SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } } diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 678a4452e00..f947c021352 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -47,6 +47,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" utilfeature "k8s.io/apiserver/pkg/util/feature" utilsysctl "k8s.io/component-helpers/node/util/sysctl" + resourcehelper "k8s.io/component-helpers/resource" schedulinghelper "k8s.io/component-helpers/scheduling/corev1" kubeletapis "k8s.io/kubelet/pkg/apis" @@ -333,7 +334,7 @@ func ValidateRuntimeClassName(name string, fldPath *field.Path) field.ErrorList // validateOverhead can be used to check whether the given Overhead is valid. func validateOverhead(overhead core.ResourceList, fldPath *field.Path, opts PodValidationOptions) field.ErrorList { // reuse the ResourceRequirements validation logic - return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, nil, fldPath, opts) + return ValidateContainerResourceRequirements(&core.ResourceRequirements{Limits: overhead}, nil, fldPath, opts) } // Validates that given value is not negative. @@ -3589,7 +3590,7 @@ func validateContainerCommon(ctr *core.Container, volumes map[string]core.Volume allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, ctr, path.Child("volumeMounts"), opts)...) allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, path.Child("volumeDevices"))...) allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, path.Child("imagePullPolicy"))...) - allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, podClaimNames, path.Child("resources"), opts)...) + allErrs = append(allErrs, ValidateContainerResourceRequirements(&ctr.Resources, podClaimNames, path.Child("resources"), opts)...) allErrs = append(allErrs, validateResizePolicy(ctr.ResizePolicy, path.Child("resizePolicy"), podRestartPolicy)...) allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, path.Child("securityContext"), hostUsers)...) return allErrs @@ -4048,6 +4049,8 @@ type PodValidationOptions struct { AllowPodLifecycleSleepActionZeroValue bool // Allow only Recursive value of SELinuxChangePolicy. AllowOnlyRecursiveSELinuxChangePolicy bool + // Indicates whether PodLevelResources feature is enabled or disabled. + PodLevelResourcesEnabled bool } // validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set, @@ -4202,6 +4205,11 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi allErrs = append(allErrs, validateContainers(spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("containers"), opts, &spec.RestartPolicy, hostUsers)...) allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("initContainers"), opts, &spec.RestartPolicy, hostUsers)...) allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts, &spec.RestartPolicy, hostUsers)...) + + if opts.PodLevelResourcesEnabled { + allErrs = append(allErrs, validatePodResources(spec, podClaimNames, fldPath.Child("resources"), opts)...) + } + allErrs = append(allErrs, validatePodHostNetworkDeps(spec, fldPath, opts)...) allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...) allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...) @@ -4282,6 +4290,77 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi return allErrs } +func validatePodResources(spec *core.PodSpec, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList { + if spec.Resources == nil { + return nil + } + + allErrs := field.ErrorList{} + + if spec.Resources.Claims != nil { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("claims"), "claims cannot be set for Resources at pod-level")) + } + + // validatePodResourceRequirements checks if resource names and quantities are + // valid, and requests are less than limits. + allErrs = append(allErrs, validatePodResourceRequirements(spec.Resources, podClaimNames, fldPath, opts)...) + allErrs = append(allErrs, validatePodResourceConsistency(spec, fldPath)...) + return allErrs +} + +// validatePodResourceConsistency checks if aggregate container-level requests are +// less than or equal to pod-level requests, and individual container-level limits +// are less than or equal to pod-level limits. +func validatePodResourceConsistency(spec *core.PodSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + // Convert the *core.PodSpec to *v1.PodSpec to satisfy the call to + // resourcehelper.PodRequests method, in the subsequent lines, + // which requires a *v1.Pod object (containing a *v1.PodSpec). + v1PodSpec := &v1.PodSpec{} + // TODO(ndixita): Convert_core_PodSpec_To_v1_PodSpec is risky. Add a copy of + // AggregateContainerRequests against internal core.Pod type for beta release of + // PodLevelResources feature. + if err := corev1.Convert_core_PodSpec_To_v1_PodSpec(spec, v1PodSpec, nil); err != nil { + allErrs = append(allErrs, field.InternalError(fldPath, fmt.Errorf("invalid %q: %v", fldPath, err.Error()))) + } + + reqPath := fldPath.Child("requests") + // resourcehelper.AggregateContainerRequests method requires a Pod object to + // calculate the total requests requirements of a pod. Hence a Pod object using + // v1PodSpec i.e. (&v1.Pod{Spec: *v1PodSpec}, is created on the fly, and passed + // to the AggregateContainerRequests method to facilitate proper resource + // calculation without modifying AggregateContainerRequests method. + aggrContainerReqs := resourcehelper.AggregateContainerRequests(&v1.Pod{Spec: *v1PodSpec}, resourcehelper.PodResourcesOptions{}) + + // Pod-level requests must be >= aggregate requests of all containers in a pod. + for resourceName, ctrReqs := range aggrContainerReqs { + key := resourceName.String() + podSpecRequests := spec.Resources.Requests[core.ResourceName(key)] + + fldPath := reqPath.Key(key) + if ctrReqs.Cmp(podSpecRequests) > 0 { + allErrs = append(allErrs, field.Invalid(fldPath, podSpecRequests.String(), fmt.Sprintf("must be greater than or equal to aggregate container requests of %s", ctrReqs.String()))) + } + } + + // Individual Container limits must be <= Pod-level limits. + for i, ctr := range spec.Containers { + for resourceName, ctrLimit := range ctr.Resources.Limits { + podSpecLimits, exists := spec.Resources.Limits[core.ResourceName(resourceName.String())] + if !exists { + continue + } + + if ctrLimit.Cmp(podSpecLimits) > 0 { + fldPath := fldPath.Child("containers").Index(i).Key(resourceName.String()).Child("limits") + allErrs = append(allErrs, field.Invalid(fldPath, ctrLimit.String(), fmt.Sprintf("must be less than or equal to pod limits of %s", podSpecLimits.String()))) + } + } + } + return allErrs +} + func validateLinux(spec *core.PodSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} securityContext := spec.SecurityContext @@ -5486,6 +5565,16 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel allErrs := ValidateImmutableField(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath) allErrs = append(allErrs, validatePodMetadataAndSpec(newPod, opts)...) + // pods with pod-level resources cannot be resized + isPodLevelResourcesSet := func(pod *core.Pod) bool { + return pod.Spec.Resources != nil && + (len(pod.Spec.Resources.Requests)+len(pod.Spec.Resources.Limits) > 0) + } + + if isPodLevelResourcesSet(oldPod) || isPodLevelResourcesSet(newPod) { + return field.ErrorList{field.Forbidden(field.NewPath(""), "pods with pod-level resources cannot be resized")} + } + // static pods cannot be resized. if _, ok := oldPod.Annotations[core.MirrorPodAnnotationKey]; ok { return field.ErrorList{field.Forbidden(field.NewPath(""), "static pods cannot be resized")} @@ -6424,6 +6513,22 @@ func validateContainerResourceName(value core.ResourceName, fldPath *field.Path) return allErrs } +// validatePodResourceName verifies that: +// 1. The resource name is a valid compute resource name for pod-level specification. +// 2. The resource is supported by the PodLevelResources feature. +func validatePodResourceName(resourceName core.ResourceName, fldPath *field.Path) field.ErrorList { + allErrs := validateResourceName(resourceName, fldPath) + if len(allErrs) != 0 { + return allErrs + } + + if !resourcehelper.IsSupportedPodLevelResource(v1.ResourceName(resourceName)) { + return append(allErrs, field.NotSupported(fldPath, resourceName, sets.List(resourcehelper.SupportedPodLevelResources()))) + } + + return allErrs +} + // Validate resource names that can go in a resource quota // Refer to docs/design/resources.md for more details. func ValidateResourceQuotaResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList { @@ -6771,8 +6876,16 @@ func validateBasicResource(quantity resource.Quantity, fldPath *field.Path) fiel return field.ErrorList{} } +func validatePodResourceRequirements(requirements *core.ResourceRequirements, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList { + return validateResourceRequirements(requirements, validatePodResourceName, podClaimNames, fldPath, opts) +} + +func ValidateContainerResourceRequirements(requirements *core.ResourceRequirements, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList { + return validateResourceRequirements(requirements, validateContainerResourceName, podClaimNames, fldPath, opts) +} + // Validates resource requirement spec. -func ValidateResourceRequirements(requirements *core.ResourceRequirements, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList { +func validateResourceRequirements(requirements *core.ResourceRequirements, resourceNameFn func(core.ResourceName, *field.Path) field.ErrorList, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList { allErrs := field.ErrorList{} limPath := fldPath.Child("limits") reqPath := fldPath.Child("requests") @@ -6785,7 +6898,7 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, podCl fldPath := limPath.Key(string(resourceName)) // Validate resource name. - allErrs = append(allErrs, validateContainerResourceName(resourceName, fldPath)...) + allErrs = append(allErrs, resourceNameFn(resourceName, fldPath)...) // Validate resource quantity. allErrs = append(allErrs, ValidateResourceQuantityValue(resourceName, quantity, fldPath)...) @@ -6804,7 +6917,8 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, podCl for resourceName, quantity := range requirements.Requests { fldPath := reqPath.Key(string(resourceName)) // Validate resource name. - allErrs = append(allErrs, validateContainerResourceName(resourceName, fldPath)...) + allErrs = append(allErrs, resourceNameFn(resourceName, fldPath)...) + // Validate resource quantity. allErrs = append(allErrs, ValidateResourceQuantityValue(resourceName, quantity, fldPath)...) diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index 636da34b2b1..b43842d3bfa 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -5758,7 +5758,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) { resource.BinarySI), }, } - if errs := ValidateResourceRequirements(&containerLimitCase, nil, field.NewPath("resources"), PodValidationOptions{}); len(errs) != 0 { + if errs := ValidateContainerResourceRequirements(&containerLimitCase, nil, field.NewPath("resources"), PodValidationOptions{}); len(errs) != 0 { t.Errorf("expected success: %v", errs) } } @@ -12299,7 +12299,6 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) { } func TestValidatePodUpdate(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true) var ( activeDeadlineSecondsZero = int64(0) activeDeadlineSecondsNegative = int64(-30) @@ -13670,7 +13669,41 @@ func TestValidatePodUpdate(t *testing.T) { err: "pod updates may not change fields other than", test: "the podAntiAffinity cannot be updated on gated pods", }, + { + new: *podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("200m", "0", "1Gi", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: *podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "0", "1Gi", ""), + }))), + ), + err: "pod updates may not change fields other than", + test: "cpu limit change with pod-level resources", + }, { + new: *podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "100Mi", "", ""), + }))), + ), + old: *podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "200Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + err: "pod updates may not change fields other than", + test: "memory limit change with pod-level resources", + }, } + for _, test := range tests { test.new.ObjectMeta.ResourceVersion = "1" test.old.ObjectMeta.ResourceVersion = "1" @@ -18643,6 +18676,260 @@ func TestValidateServiceUpdate(t *testing.T) { } } +func TestValidatePodResourceConsistency(t *testing.T) { + path := field.NewPath("resources") + tests := []struct { + name string + podResources core.ResourceRequirements + containers []core.Container + expectedErrors []string + }{{ + name: "aggregate container requests less than pod requests", + podResources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, { + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("4"), + core.ResourceMemory: resource.MustParse("3Mi"), + }, + }, + }, + }, + }, { + name: "aggregate container requests equal to pod requests", + podResources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, { + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, + }, + }, { + name: "aggregate container requests greater than pod requests", + podResources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("6"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, { + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("8"), + core.ResourceMemory: resource.MustParse("3Mi"), + }, + }, + }, + }, + expectedErrors: []string{"must be greater than or equal to aggregate container requests"}, + }, { + name: "aggregate container limits less than pod limits", + podResources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("4"), + core.ResourceMemory: resource.MustParse("3Mi"), + }, + }, + }, + }, + }, { + name: "aggregate container limits equal to pod limits", + podResources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, + }, + }, { + name: "aggregate container limits greater than pod limits", + podResources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("5"), + core.ResourceMemory: resource.MustParse("5Mi"), + }, + }, + }, { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("6"), + core.ResourceMemory: resource.MustParse("9Mi"), + }, + }, + }, + }, + }, { + name: "indivdual container limits greater than pod limits", + podResources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + core.ResourceMemory: resource.MustParse("10Mi"), + }, + }, + containers: []core.Container{ + { + Resources: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("11"), + core.ResourceMemory: resource.MustParse("12Mi"), + }, + }, + }, + }, + expectedErrors: []string{ + "must be less than or equal to pod limits", + "must be less than or equal to pod limits", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + spec := core.PodSpec{ + Resources: &tc.podResources, + Containers: tc.containers, + } + errs := validatePodResourceConsistency(&spec, path) + if len(errs) != len(tc.expectedErrors) { + t.Errorf("expected %d errors, got %d errors, got errors: %v", len(tc.expectedErrors), len(errs), errs) + } + + for _, expectedErr := range tc.expectedErrors { + expectedErrExists := false + for _, gotErr := range errs { + if strings.Contains(gotErr.Error(), expectedErr) { + expectedErrExists = true + break + } + } + + if !expectedErrExists { + t.Errorf("expected: %v, got errors: %v", expectedErr, errs) + } + } + }) + } +} + +func TestValidatePodResourceNames(t *testing.T) { + table := []struct { + input core.ResourceName + expectedFailure bool + }{ + {"memory", false}, + {"cpu", false}, + {"storage", true}, + {"requests.cpu", true}, + {"requests.memory", true}, + {"requests.storage", true}, + {"limits.cpu", true}, + {"limits.memory", true}, + {"limits.storage", true}, + {"network", true}, + {"disk", true}, + {"", true}, + {".", true}, + {"..", true}, + {"my.favorite.app.co/12345", true}, + {"my.favorite.app.co/_12345", true}, + {"my.favorite.app.co/12345_", true}, + {"kubernetes.io/..", true}, + {core.ResourceName("kubernetes.io/" + strings.Repeat("a", 64)), true}, + {core.ResourceName("kubernetes.io/" + strings.Repeat("a", 64)), true}, + {core.ResourceName("kubernetes.io/" + core.ResourceCPU), true}, + {core.ResourceName("kubernetes.io/" + core.ResourceMemory), true}, + {"kubernetes.io//", true}, + {"kubernetes.io", true}, + {"kubernetes.io/will/not/work/", true}, + } + for _, item := range table { + errs := validatePodResourceName(item.input, field.NewPath("field")) + if len(errs) != 0 && !item.expectedFailure { + t.Errorf("expected no failure for input %q, got: %v", item.input, errs) + } + + if len(errs) == 0 && item.expectedFailure { + t.Errorf("expected failure for input %q", item.input) + } + } +} + func TestValidateResourceNames(t *testing.T) { table := []struct { input core.ResourceName @@ -20656,6 +20943,7 @@ func TestValidateOSFields(t *testing.T) { "ResourceClaims[*].Name", "ResourceClaims[*].ResourceClaimName", "ResourceClaims[*].ResourceClaimTemplateName", + "Resources", "RestartPolicy", "RuntimeClassName", "SchedulerName", @@ -23082,10 +23370,14 @@ func TestValidatePodTemplateSpecSeccomp(t *testing.T) { func TestValidateResourceRequirements(t *testing.T) { path := field.NewPath("resources") + // TODO(ndixita): refactor the tests to check the expected errors are equal to + // got errors. tests := []struct { name string requirements core.ResourceRequirements - opts PodValidationOptions + validateFn func(requirements *core.ResourceRequirements, + podClaimNames sets.Set[string], fldPath *field.Path, + opts PodValidationOptions) field.ErrorList }{{ name: "limits and requests of hugepage resource are equal", requirements: core.ResourceRequirements{ @@ -23098,7 +23390,7 @@ func TestValidateResourceRequirements(t *testing.T) { core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"), }, }, - opts: PodValidationOptions{}, + validateFn: ValidateContainerResourceRequirements, }, { name: "limits and requests of memory resource are equal", requirements: core.ResourceRequirements{ @@ -23109,7 +23401,7 @@ func TestValidateResourceRequirements(t *testing.T) { core.ResourceMemory: resource.MustParse("2Mi"), }, }, - opts: PodValidationOptions{}, + validateFn: ValidateContainerResourceRequirements, }, { name: "limits and requests of cpu resource are equal", requirements: core.ResourceRequirements{ @@ -23120,13 +23412,36 @@ func TestValidateResourceRequirements(t *testing.T) { core.ResourceCPU: resource.MustParse("10"), }, }, - opts: PodValidationOptions{}, + validateFn: ValidateContainerResourceRequirements, }, + { + name: "limits and requests of memory resource are equal", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceMemory: resource.MustParse("2Mi"), + }, + Requests: core.ResourceList{ + core.ResourceMemory: resource.MustParse("2Mi"), + }, + }, + validateFn: validatePodResourceRequirements, + }, { + name: "limits and requests of cpu resource are equal", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + }, + Requests: core.ResourceList{ + core.ResourceCPU: resource.MustParse("10"), + }, + }, + validateFn: validatePodResourceRequirements, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - if errs := ValidateResourceRequirements(&tc.requirements, nil, path, tc.opts); len(errs) != 0 { + if errs := tc.validateFn(&tc.requirements, nil, path, PodValidationOptions{}); len(errs) != 0 { t.Errorf("unexpected errors: %v", errs) } }) @@ -23135,7 +23450,9 @@ func TestValidateResourceRequirements(t *testing.T) { errTests := []struct { name string requirements core.ResourceRequirements - opts PodValidationOptions + validateFn func(requirements *core.ResourceRequirements, + podClaimNames sets.Set[string], fldPath *field.Path, + opts PodValidationOptions) field.ErrorList }{{ name: "hugepage resource without cpu or memory", requirements: core.ResourceRequirements{ @@ -23146,13 +23463,69 @@ func TestValidateResourceRequirements(t *testing.T) { core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"), }, }, - opts: PodValidationOptions{}, + validateFn: ValidateContainerResourceRequirements, + }, { + name: "pod resource with hugepages", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"), + }, + Requests: core.ResourceList{ + core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"), + }, + }, + validateFn: validatePodResourceRequirements, + }, { + name: "pod resource with ephemeral-storage", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceName(core.ResourceEphemeralStorage): resource.MustParse("2Mi"), + }, + Requests: core.ResourceList{ + core.ResourceName(core.ResourceEphemeralStorage + "2Mi"): resource.MustParse("2Mi"), + }, + }, + validateFn: validatePodResourceRequirements, + }, { + name: "pod resource with unsupported prefixed resources", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceName("kubernetesio/" + core.ResourceCPU): resource.MustParse("2"), + }, + Requests: core.ResourceList{ + core.ResourceName("kubernetesio/" + core.ResourceMemory): resource.MustParse("2"), + }, + }, + validateFn: validatePodResourceRequirements, + }, { + name: "pod resource with unsupported native resources", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceName("kubernetes.io/" + strings.Repeat("a", 63)): resource.MustParse("2"), + }, + Requests: core.ResourceList{ + core.ResourceName("kubernetes.io/" + strings.Repeat("a", 63)): resource.MustParse("2"), + }, + }, + validateFn: validatePodResourceRequirements, }, + { + name: "pod resource with unsupported empty native resource name", + requirements: core.ResourceRequirements{ + Limits: core.ResourceList{ + core.ResourceName("kubernetes.io/"): resource.MustParse("2"), + }, + Requests: core.ResourceList{ + core.ResourceName("kubernetes.io"): resource.MustParse("2"), + }, + }, + validateFn: validatePodResourceRequirements, + }, } for _, tc := range errTests { t.Run(tc.name, func(t *testing.T) { - if errs := ValidateResourceRequirements(&tc.requirements, nil, path, tc.opts); len(errs) == 0 { + if errs := tc.validateFn(&tc.requirements, nil, path, PodValidationOptions{}); len(errs) == 0 { t.Error("expected errors") } }) @@ -25112,6 +25485,148 @@ func TestValidatePodResize(t *testing.T) { new *core.Pod err string }{ + { + test: "pod-level resources with container cpu limit change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("200m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, { + test: "pod-level resources with container memory limit change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "200Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, + { + test: "pod-level resources with container cpu request change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "200Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("200m", "200Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, { + test: "pod-level resources with container memory request change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "200Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, + { + test: "pod-level resources with pod-level memory limit change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("200m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("200m", "100Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, + { + test: "pod-level resources with pod-level memory request change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("200m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("200m", "100Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, + { + test: "pod-level resources with pod-level cpu limit change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("200m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, + { + test: "pod-level resources with pod-level cpu request change", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("200m", "200Mi", "", "")}), + ), + err: "pods with pod-level resources cannot be resized", + }, { test: "cpu limit change", old: mkPod(core.ResourceList{}, getResources("100m", "0", "1Gi", "")), diff --git a/pkg/apis/core/zz_generated.deepcopy.go b/pkg/apis/core/zz_generated.deepcopy.go index ef70c1fb32f..a9d32c1ca69 100644 --- a/pkg/apis/core/zz_generated.deepcopy.go +++ b/pkg/apis/core/zz_generated.deepcopy.go @@ -4373,6 +4373,11 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(ResourceRequirements) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/extensions/v1beta1/zz_generated.defaults.go b/pkg/apis/extensions/v1beta1/zz_generated.defaults.go index 122455427fa..bfe82d2deef 100644 --- a/pkg/apis/extensions/v1beta1/zz_generated.defaults.go +++ b/pkg/apis/extensions/v1beta1/zz_generated.defaults.go @@ -341,6 +341,10 @@ func SetObjectDefaults_DaemonSet(in *extensionsv1beta1.DaemonSet) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DaemonSetList(in *extensionsv1beta1.DaemonSetList) { @@ -646,6 +650,10 @@ func SetObjectDefaults_Deployment(in *extensionsv1beta1.Deployment) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_DeploymentList(in *extensionsv1beta1.DeploymentList) { @@ -981,6 +989,10 @@ func SetObjectDefaults_ReplicaSet(in *extensionsv1beta1.ReplicaSet) { } } corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead) + if in.Spec.Template.Spec.Resources != nil { + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Limits) + corev1.SetDefaults_ResourceList(&in.Spec.Template.Spec.Resources.Requests) + } } func SetObjectDefaults_ReplicaSetList(in *extensionsv1beta1.ReplicaSetList) { diff --git a/pkg/apis/node/validation/validation.go b/pkg/apis/node/validation/validation.go index 4e0a20aaf1e..d66e82b9382 100644 --- a/pkg/apis/node/validation/validation.go +++ b/pkg/apis/node/validation/validation.go @@ -54,7 +54,7 @@ func ValidateRuntimeClassUpdate(new, old *node.RuntimeClass) field.ErrorList { func validateOverhead(overhead *node.Overhead, fldPath *field.Path) field.ErrorList { // reuse the ResourceRequirements validation logic - return corevalidation.ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead.PodFixed}, nil, fldPath, + return corevalidation.ValidateContainerResourceRequirements(&core.ResourceRequirements{Limits: overhead.PodFixed}, nil, fldPath, corevalidation.PodValidationOptions{}) } diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 9f15b2b3ee6..5411f576031 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -852,6 +852,13 @@ const ( // Enables external service account JWT signing and key management. // If enabled, it allows passing --service-account-signing-endpoint flag to configure external signer. ExternalServiceAccountTokenSigner featuregate.Feature = "ExternalServiceAccountTokenSigner" + + // owner: @ndixita + // key: https://kep.k8s.io/2837 + // alpha: 1.32 + // + // Enables specifying resources at pod-level. + PodLevelResources featuregate.Feature = "PodLevelResources" ) func init() { diff --git a/pkg/features/versioned_kube_features.go b/pkg/features/versioned_kube_features.go index a0a792fe0a3..360304cf95d 100644 --- a/pkg/features/versioned_kube_features.go +++ b/pkg/features/versioned_kube_features.go @@ -580,6 +580,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.35 }, + PodLevelResources: { + {Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha}, + }, + PodLifecycleSleepAction: { {Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta}, diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 60e52ccaf4a..455da7915f8 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -28583,12 +28583,18 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA }, }, }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, }, Required: []string{"containers"}, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodOS", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodResourceClaim", "k8s.io/api/core/v1.PodSchedulingGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, } } diff --git a/pkg/kubelet/cm/helpers_linux.go b/pkg/kubelet/cm/helpers_linux.go index 5500621c9ab..9ea190aba71 100644 --- a/pkg/kubelet/cm/helpers_linux.go +++ b/pkg/kubelet/cm/helpers_linux.go @@ -118,16 +118,20 @@ func HugePageLimits(resourceList v1.ResourceList) map[int64]int64 { // ResourceConfigForPod takes the input pod and outputs the cgroup resource config. func ResourceConfigForPod(allocatedPod *v1.Pod, enforceCPULimits bool, cpuPeriod uint64, enforceMemoryQoS bool) *ResourceConfig { + podLevelResourcesEnabled := utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodLevelResources) + // sum requests and limits. reqs := resource.PodRequests(allocatedPod, resource.PodResourcesOptions{ - // pod is already configured to the allocated resources, and we explicitly don't want to use - // the actual resources if we're instantiating a resize. - UseStatusResources: false, + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !podLevelResourcesEnabled, + UseStatusResources: false, }) // track if limits were applied for each resource. memoryLimitsDeclared := true cpuLimitsDeclared := true limits := resource.PodLimits(allocatedPod, resource.PodResourcesOptions{ + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !podLevelResourcesEnabled, ContainerFn: func(res v1.ResourceList, containerType resource.ContainerType) { if res.Cpu().IsZero() { cpuLimitsDeclared = false @@ -137,6 +141,16 @@ func ResourceConfigForPod(allocatedPod *v1.Pod, enforceCPULimits bool, cpuPeriod } }, }) + + if podLevelResourcesEnabled && resource.IsPodLevelResourcesSet(allocatedPod) { + if !allocatedPod.Spec.Resources.Limits.Cpu().IsZero() { + cpuLimitsDeclared = true + } + + if !allocatedPod.Spec.Resources.Limits.Memory().IsZero() { + memoryLimitsDeclared = true + } + } // map hugepage pagesize (bytes) to limits (bytes) hugePageLimits := HugePageLimits(reqs) diff --git a/pkg/kubelet/cm/helpers_linux_test.go b/pkg/kubelet/cm/helpers_linux_test.go index 1399a2acf0c..92bab254975 100644 --- a/pkg/kubelet/cm/helpers_linux_test.go +++ b/pkg/kubelet/cm/helpers_linux_test.go @@ -70,10 +70,11 @@ func TestResourceConfigForPod(t *testing.T) { cpuNoLimit := int64(-1) guaranteedMemory := memoryQuantity.Value() testCases := map[string]struct { - pod *v1.Pod - expected *ResourceConfig - enforceCPULimits bool - quotaPeriod uint64 // in microseconds + pod *v1.Pod + expected *ResourceConfig + enforceCPULimits bool + quotaPeriod uint64 // in microseconds + podLevelResourcesEnabled bool }{ "besteffort": { pod: &v1.Pod{ @@ -274,12 +275,126 @@ func TestResourceConfigForPod(t *testing.T) { quotaPeriod: tunedQuotaPeriod, expected: &ResourceConfig{CPUShares: &burstablePartialShares}, }, + "burstable-with-pod-level-requests": { + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: getResourceList("100m", "100Mi"), + }, + Containers: []v1.Container{ + { + Name: "Container with no resources", + }, + }, + }, + }, + podLevelResourcesEnabled: true, + enforceCPULimits: true, + quotaPeriod: defaultQuotaPeriod, + expected: &ResourceConfig{CPUShares: &burstableShares}, + }, + "burstable-with-pod-and-container-level-requests": { + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: getResourceList("100m", "100Mi"), + }, + Containers: []v1.Container{ + { + Name: "Container with resources", + Resources: getResourceRequirements(getResourceList("10m", "50Mi"), getResourceList("", "")), + }, + }, + }, + }, + podLevelResourcesEnabled: true, + enforceCPULimits: true, + quotaPeriod: defaultQuotaPeriod, + expected: &ResourceConfig{CPUShares: &burstableShares}, + }, + "burstable-with-pod-level-resources": { + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: getResourceList("100m", "100Mi"), + Limits: getResourceList("200m", "200Mi"), + }, + Containers: []v1.Container{ + { + Name: "Container with no resources", + }, + }, + }, + }, + podLevelResourcesEnabled: true, + enforceCPULimits: true, + quotaPeriod: defaultQuotaPeriod, + expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory}, + }, + "burstable-with-pod-and-container-level-resources": { + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: getResourceList("100m", "100Mi"), + Limits: getResourceList("200m", "200Mi"), + }, + Containers: []v1.Container{ + { + Name: "Container with resources", + Resources: getResourceRequirements(getResourceList("10m", "50Mi"), getResourceList("50m", "100Mi")), + }, + }, + }, + }, + podLevelResourcesEnabled: true, + enforceCPULimits: true, + quotaPeriod: defaultQuotaPeriod, + expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory}, + }, + "guaranteed-with-pod-level-resources": { + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: getResourceList("100m", "100Mi"), + Limits: getResourceList("100m", "100Mi"), + }, + Containers: []v1.Container{ + { + Name: "Container with no resources", + }, + }, + }, + }, + podLevelResourcesEnabled: true, + enforceCPULimits: true, + quotaPeriod: defaultQuotaPeriod, + expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory}, + }, + "guaranteed-with-pod-and-container-level-resources": { + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: getResourceList("100m", "100Mi"), + Limits: getResourceList("100m", "100Mi"), + }, + Containers: []v1.Container{ + { + Name: "Container with resources", + Resources: getResourceRequirements(getResourceList("10m", "50Mi"), getResourceList("50m", "100Mi")), + }, + }, + }, + }, + podLevelResourcesEnabled: true, + enforceCPULimits: true, + quotaPeriod: defaultQuotaPeriod, + expected: &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory}, + }, } for testName, testCase := range testCases { - + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.PodLevelResources, testCase.podLevelResourcesEnabled) actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, false) - if !reflect.DeepEqual(actual.CPUPeriod, testCase.expected.CPUPeriod) { t.Errorf("unexpected result, test: %v, cpu period not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUPeriod, *actual.CPUPeriod) } @@ -287,7 +402,7 @@ func TestResourceConfigForPod(t *testing.T) { t.Errorf("unexpected result, test: %v, cpu quota not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUQuota, *actual.CPUQuota) } if !reflect.DeepEqual(actual.CPUShares, testCase.expected.CPUShares) { - t.Errorf("unexpected result, test: %v, cpu shares not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUShares, &actual.CPUShares) + t.Errorf("unexpected result, test: %v, cpu shares not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUShares, *actual.CPUShares) } if !reflect.DeepEqual(actual.Memory, testCase.expected.Memory) { t.Errorf("unexpected result, test: %v, memory not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.Memory, *actual.Memory) diff --git a/pkg/kubelet/cm/qos_container_manager_linux.go b/pkg/kubelet/cm/qos_container_manager_linux.go index 441aa952b65..0f88e10ff69 100644 --- a/pkg/kubelet/cm/qos_container_manager_linux.go +++ b/pkg/kubelet/cm/qos_container_manager_linux.go @@ -179,7 +179,11 @@ func (m *qosContainerManagerImpl) setCPUCgroupConfig(configs map[v1.PodQOSClass] // we only care about the burstable qos tier continue } - req := resource.PodRequests(pod, resource.PodResourcesOptions{Reuse: reuseReqs}) + req := resource.PodRequests(pod, resource.PodResourcesOptions{ + Reuse: reuseReqs, + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodLevelResources), + }) if request, found := req[v1.ResourceCPU]; found { burstablePodCPURequest += request.MilliValue() } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go index 06ec537233c..20d7985f365 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go @@ -35,6 +35,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" utilfeature "k8s.io/apiserver/pkg/util/feature" + resourcehelper "k8s.io/component-helpers/resource" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" "k8s.io/klog/v2" @@ -94,6 +95,34 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.C return lc, nil } +// getCPULimit returns the memory limit for the container to be used to calculate +// Linux Container Resources. +func getCPULimit(pod *v1.Pod, container *v1.Container) *resource.Quantity { + if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodLevelResources) && resourcehelper.IsPodLevelResourcesSet(pod) { + // When container-level CPU limit is not set, the pod-level + // limit is used in the calculation for components relying on linux resource limits + // to be set. + if container.Resources.Limits.Cpu().IsZero() { + return pod.Spec.Resources.Limits.Cpu() + } + } + return container.Resources.Limits.Cpu() +} + +// getMemoryLimit returns the memory limit for the container to be used to calculate +// Linux Container Resources. +func getMemoryLimit(pod *v1.Pod, container *v1.Container) *resource.Quantity { + if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodLevelResources) && resourcehelper.IsPodLevelResourcesSet(pod) { + // When container-level memory limit is not set, the pod-level + // limit is used in the calculation for components relying on linux resource limits + // to be set. + if container.Resources.Limits.Memory().IsZero() { + return pod.Spec.Resources.Limits.Memory() + } + } + return container.Resources.Limits.Memory() +} + // generateLinuxContainerResources generates linux container resources config for runtime func (m *kubeGenericRuntimeManager) generateLinuxContainerResources(pod *v1.Pod, container *v1.Container, enforceMemoryQoS bool) *runtimeapi.LinuxContainerResources { // set linux container resources @@ -101,7 +130,10 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerResources(pod *v1.Pod, if _, cpuRequestExists := container.Resources.Requests[v1.ResourceCPU]; cpuRequestExists { cpuRequest = container.Resources.Requests.Cpu() } - lcr := m.calculateLinuxResources(cpuRequest, container.Resources.Limits.Cpu(), container.Resources.Limits.Memory()) + + memoryLimit := getMemoryLimit(pod, container) + cpuLimit := getCPULimit(pod, container) + lcr := m.calculateLinuxResources(cpuRequest, cpuLimit, memoryLimit) lcr.OomScoreAdj = int64(qos.GetContainerOOMScoreAdjust(pod, container, int64(m.machineInfo.MemoryCapacity))) diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go index b9425b61efa..69960b72d28 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go @@ -167,13 +167,14 @@ func TestGenerateLinuxContainerConfigResources(t *testing.T) { assert.NoError(t, err) tests := []struct { - name string - podResources v1.ResourceRequirements - expected *runtimeapi.LinuxContainerResources + name string + containerResources v1.ResourceRequirements + podResources *v1.ResourceRequirements + expected *runtimeapi.LinuxContainerResources }{ { name: "Request 128M/1C, Limit 256M/3C", - podResources: v1.ResourceRequirements{ + containerResources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceMemory: resource.MustParse("128Mi"), v1.ResourceCPU: resource.MustParse("1"), @@ -192,7 +193,7 @@ func TestGenerateLinuxContainerConfigResources(t *testing.T) { }, { name: "Request 128M/2C, No Limit", - podResources: v1.ResourceRequirements{ + containerResources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceMemory: resource.MustParse("128Mi"), v1.ResourceCPU: resource.MustParse("2"), @@ -205,6 +206,27 @@ func TestGenerateLinuxContainerConfigResources(t *testing.T) { MemoryLimitInBytes: 0, }, }, + { + name: "Container Level Request 128M/1C, Pod Level Limit 256M/3C", + containerResources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("128Mi"), + v1.ResourceCPU: resource.MustParse("1"), + }, + }, + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("256Mi"), + v1.ResourceCPU: resource.MustParse("3"), + }, + }, + expected: &runtimeapi.LinuxContainerResources{ + CpuPeriod: 100000, + CpuQuota: 300000, + CpuShares: 1024, + MemoryLimitInBytes: 256 * 1024 * 1024, + }, + }, } for _, test := range tests { @@ -222,12 +244,17 @@ func TestGenerateLinuxContainerConfigResources(t *testing.T) { ImagePullPolicy: v1.PullIfNotPresent, Command: []string{"testCommand"}, WorkingDir: "testWorkingDir", - Resources: test.podResources, + Resources: test.containerResources, }, }, }, } + if test.podResources != nil { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, true) + pod.Spec.Resources = test.podResources + } + linuxConfig, err := m.generateLinuxContainerConfig(&pod.Spec.Containers[0], pod, new(int64), "", nil, false) assert.NoError(t, err) assert.Equal(t, test.expected.CpuPeriod, linuxConfig.GetResources().CpuPeriod, test.name) diff --git a/pkg/kubelet/kuberuntime/kuberuntime_sandbox_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_sandbox_linux.go index 8415103737a..6345e3af0a0 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_sandbox_linux.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_sandbox_linux.go @@ -22,7 +22,9 @@ package kuberuntime import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + utilfeature "k8s.io/apiserver/pkg/util/feature" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" + "k8s.io/kubernetes/pkg/features" resourcehelper "k8s.io/component-helpers/resource" ) @@ -44,6 +46,8 @@ func (m *kubeGenericRuntimeManager) convertOverheadToLinuxResources(pod *v1.Pod) func (m *kubeGenericRuntimeManager) calculateSandboxResources(pod *v1.Pod) *runtimeapi.LinuxContainerResources { opts := resourcehelper.PodResourcesOptions{ ExcludeOverhead: true, + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources), } req := resourcehelper.PodRequests(pod, opts) lim := resourcehelper.PodLimits(pod, opts) diff --git a/pkg/kubelet/qos/helpers.go b/pkg/kubelet/qos/helpers.go index 2b327e5a7d3..17ed12e3b33 100644 --- a/pkg/kubelet/qos/helpers.go +++ b/pkg/kubelet/qos/helpers.go @@ -27,6 +27,7 @@ package qos // import "k8s.io/kubernetes/pkg/kubelet/qos" import ( v1 "k8s.io/api/core/v1" + resourcehelper "k8s.io/component-helpers/resource" ) // minRegularContainerMemory returns the minimum memory resource quantity @@ -41,3 +42,30 @@ func minRegularContainerMemory(pod v1.Pod) int64 { } return memoryValue } + +// remainingPodMemReqPerContainer calculates the remaining pod memory request per +// container by: +// 1. Taking the total pod memory requests +// 2. Subtracting total container memory requests from pod memory requests +// 3. Dividing the remainder by the number of containers. +// This gives us the additional memory request that is not allocated to any +// containers in the pod. This value will be divided equally among all containers to +// calculate oom score adjusment. +// See https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/2837-pod-level-resource-spec/README.md#oom-score-adjustment +// for more details. +func remainingPodMemReqPerContainer(pod *v1.Pod) int64 { + var remainingMemory int64 + if pod.Spec.Resources.Requests.Memory().IsZero() { + return remainingMemory + } + + numContainers := len(pod.Spec.Containers) + len(pod.Spec.InitContainers) + + // Aggregated requests of all containers. + aggrContainerReqs := resourcehelper.AggregateContainerRequests(pod, resourcehelper.PodResourcesOptions{}) + + remainingMemory = pod.Spec.Resources.Requests.Memory().Value() - aggrContainerReqs.Memory().Value() + + remainingMemoryPerContainer := remainingMemory / int64(numContainers) + return remainingMemoryPerContainer +} diff --git a/pkg/kubelet/qos/policy.go b/pkg/kubelet/qos/policy.go index c4beb95d410..f12da0e5bfb 100644 --- a/pkg/kubelet/qos/policy.go +++ b/pkg/kubelet/qos/policy.go @@ -19,6 +19,7 @@ package qos import ( v1 "k8s.io/api/core/v1" utilfeature "k8s.io/apiserver/pkg/util/feature" + resourcehelper "k8s.io/component-helpers/resource" v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/types" @@ -63,14 +64,41 @@ func GetContainerOOMScoreAdjust(pod *v1.Pod, container *v1.Container, memoryCapa // which use more than their request will have an OOM score of 1000 and will be prime // targets for OOM kills. // Note that this is a heuristic, it won't work if a container has many small processes. - memoryRequest := container.Resources.Requests.Memory().Value() - oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity + containerMemReq := container.Resources.Requests.Memory().Value() + + var oomScoreAdjust, remainingReqPerContainer int64 + // When PodLevelResources feature is enabled, the OOM score adjustment formula is modified + // to account for pod-level memory requests. Any extra pod memory request that's + // not allocated to the containers is divided equally among all containers and + // added to their individual memory requests when calculating the OOM score + // adjustment. Otherwise, only container-level memory requests are used. See + // https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/2837-pod-level-resource-spec/README.md#oom-score-adjustment + // for more details. + if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) && + resourcehelper.IsPodLevelRequestsSet(pod) { + // TODO(ndixita): Refactor to use this formula in all cases, as + // remainingReqPerContainer will be 0 when pod-level resources are not set. + remainingReqPerContainer = remainingPodMemReqPerContainer(pod) + oomScoreAdjust = 1000 - (1000 * (containerMemReq + remainingReqPerContainer) / memoryCapacity) + } else { + oomScoreAdjust = 1000 - (1000*containerMemReq)/memoryCapacity + } // adapt the sidecarContainer memoryRequest for OOM ADJ calculation // calculate the oom score adjustment based on: max-memory( currentSideCarContainer , min-memory(regular containers) ) . if utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) && isSidecarContainer(pod, container) { // check min memory quantity in regular containers minMemoryRequest := minRegularContainerMemory(*pod) + + // When calculating minMemoryOomScoreAdjust for sidecar containers with PodLevelResources enabled, + // we add the per-container share of unallocated pod memory requests to the minimum memory request. + // This ensures the OOM score adjustment i.e. minMemoryOomScoreAdjust + // calculation remains consistent + // with how we handle pod-level memory requests for regular containers. + if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) && + resourcehelper.IsPodLevelRequestsSet(pod) { + minMemoryRequest += remainingReqPerContainer + } minMemoryOomScoreAdjust := 1000 - (1000*minMemoryRequest)/memoryCapacity // the OOM adjustment for sidecar container will match // or fall below the OOM score adjustment of regular containers in the Pod. diff --git a/pkg/kubelet/qos/policy_test.go b/pkg/kubelet/qos/policy_test.go index c09cb1e6891..a7a7980f3f9 100644 --- a/pkg/kubelet/qos/policy_test.go +++ b/pkg/kubelet/qos/policy_test.go @@ -177,8 +177,10 @@ var ( }, }, } - sampleDefaultMemRequest = resource.MustParse(strconv.FormatInt(standardMemoryAmount/8, 10)) - sampleDefaultMemLimit = resource.MustParse(strconv.FormatInt(1000+(standardMemoryAmount/8), 10)) + sampleDefaultMemRequest = resource.MustParse(strconv.FormatInt(standardMemoryAmount/8, 10)) + sampleDefaultMemLimit = resource.MustParse(strconv.FormatInt(1000+(standardMemoryAmount/8), 10)) + sampleDefaultPodMemRequest = resource.MustParse(strconv.FormatInt(standardMemoryAmount/4, 10)) + sampleDefaultPodMemLimit = resource.MustParse(strconv.FormatInt(1000+(standardMemoryAmount/4), 10)) sampleContainer = v1.Container{ Name: "main-1", @@ -300,6 +302,347 @@ var ( }, }, } + + // Pod definitions with their resource specifications are defined in this section. + // TODO(ndixita): cleanup the tests to create a method that generates pod + // definitions based on input resource parameters, replacing the current + // approach of individual pod variables. + guaranteedPodResourcesNoContainerResources = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + }, + Containers: []v1.Container{ + { + Name: "no-request-limit-1", + Resources: v1.ResourceRequirements{}, + }, + { + Name: "no-request-limit-2", + Resources: v1.ResourceRequirements{}, + }, + }, + }, + } + + guaranteedPodResourcesEqualContainerRequests = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + }, + Containers: []v1.Container{ + { + Name: "guaranteed-container-1", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + }, + }, + { + Name: "guaranteed-container-2", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + }, + }, + }, + }, + } + + guaranteedPodResourcesUnequalContainerRequests = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + }, + Containers: []v1.Container{ + { + Name: "burstable-container", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("3m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemLimit, + }, + }, + }, + { + Name: "best-effort-container", + Resources: v1.ResourceRequirements{}, + }, + }, + }, + } + + burstablePodResourcesNoContainerResources = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + Containers: []v1.Container{ + { + Name: "no-request-limit-1", + Resources: v1.ResourceRequirements{}, + }, + { + Name: "no-request-limit-2", + Resources: v1.ResourceRequirements{}, + }, + }, + }, + } + + burstablePodResourcesEqualContainerRequests = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + Containers: []v1.Container{ + { + Name: "guaranteed-container-1", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + }, + }, + { + Name: "guaranteed-container-2", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + }, + }, + }, + }, + } + + burstablePodResourcesUnequalContainerRequests = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + Containers: []v1.Container{ + { + Name: "burstable-container", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("3m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + }, + }, + { + Name: "best-effort-container", + Resources: v1.ResourceRequirements{}, + }, + }, + }, + } + + burstablePodResourcesNoContainerResourcesWithSidecar = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + Containers: []v1.Container{ + { + Name: "no-request-limit", + Resources: v1.ResourceRequirements{}, + }, + }, + InitContainers: []v1.Container{ + { + Name: "no-request-limit-sidecar", + Resources: v1.ResourceRequirements{}, + RestartPolicy: &restartPolicyAlways, + }, + }, + }, + } + + burstablePodResourcesEqualContainerRequestsWithSidecar = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + Containers: []v1.Container{ + { + Name: "burstable-container", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemLimit, + }, + }, + }, + }, + InitContainers: []v1.Container{ + { + Name: "burstable-sidecar", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemRequest, + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultMemLimit, + }, + }, + RestartPolicy: &restartPolicyAlways, + }, + }, + }, + } + + burstablePodResourcesUnequalContainerRequestsWithSidecar = v1.Pod{ + Spec: v1.PodSpec{ + Resources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: resource.MustParse("2000000000"), + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + Containers: []v1.Container{ + { + Name: "burstable-container-1", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: resource.MustParse("1000000000"), + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + }, + { + Name: "burstable-container-2", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: resource.MustParse("500000000"), + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + }, + }, + InitContainers: []v1.Container{ + { + Name: "burstable-sidecar", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: resource.MustParse("200000000"), + }, + Limits: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("5m"), + v1.ResourceMemory: sampleDefaultPodMemLimit, + }, + }, + RestartPolicy: &restartPolicyAlways, + }, + }, + }, + } ) type lowHighOOMScoreAdjTest struct { @@ -311,6 +654,7 @@ type oomTest struct { memoryCapacity int64 lowHighOOMScoreAdj map[string]lowHighOOMScoreAdjTest // [container-name] : min and max oom_score_adj score the container should be assigned. sidecarContainersFeatureEnabled bool + podLevelResourcesFeatureEnabled bool } func TestGetContainerOOMScoreAdjust(t *testing.T) { @@ -425,10 +769,96 @@ func TestGetContainerOOMScoreAdjust(t *testing.T) { }, sidecarContainersFeatureEnabled: true, }, + "guaranteed-pod-resources-no-container-resources": { + pod: &guaranteedPodResourcesNoContainerResources, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "no-request-limit-1": {lowOOMScoreAdj: -997, highOOMScoreAdj: -997}, + "no-request-limit-2": {lowOOMScoreAdj: -997, highOOMScoreAdj: -997}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + }, + "guaranteed-pod-resources-equal-container-resources": { + pod: &guaranteedPodResourcesEqualContainerRequests, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "guaranteed-container-1": {lowOOMScoreAdj: -997, highOOMScoreAdj: -997}, + "guaranteed-container-2": {lowOOMScoreAdj: -997, highOOMScoreAdj: -997}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + }, + "guaranteed-pod-resources-unequal-container-requests": { + pod: &guaranteedPodResourcesUnequalContainerRequests, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "burstable-container": {lowOOMScoreAdj: -997, highOOMScoreAdj: -997}, + "best-effort-container": {lowOOMScoreAdj: -997, highOOMScoreAdj: -997}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + }, + "burstable-pod-resources-no-container-resources": { + pod: &burstablePodResourcesNoContainerResources, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "no-request-limit-1": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + "no-request-limit-2": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + }, + "burstable-pod-resources-equal-container-requests": { + pod: &burstablePodResourcesEqualContainerRequests, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "guaranteed-container-1": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + "guaranteed-container-2": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + }, + "burstable-pod-resources-unequal-container-requests": { + pod: &burstablePodResourcesUnequalContainerRequests, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "burstable-container": {lowOOMScoreAdj: 625, highOOMScoreAdj: 625}, + "best-effort-container": {lowOOMScoreAdj: 875, highOOMScoreAdj: 875}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + }, + "burstable-pod-resources-no-container-resources-with-sidecar": { + pod: &burstablePodResourcesNoContainerResourcesWithSidecar, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "no-request-limit": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + "no-request-limit-sidecar": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + sidecarContainersFeatureEnabled: true, + }, + "burstable-pod-resources-equal-container-requests-with-sidecar": { + pod: &burstablePodResourcesEqualContainerRequestsWithSidecar, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "burstable-container": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + "burstable-sidecar": {lowOOMScoreAdj: 750, highOOMScoreAdj: 750}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + sidecarContainersFeatureEnabled: true, + }, + "burstable-pod-resources-unequal-container-requests-with-sidecar": { + pod: &burstablePodResourcesUnequalContainerRequestsWithSidecar, + lowHighOOMScoreAdj: map[string]lowHighOOMScoreAdjTest{ + "burstable-container-1": {lowOOMScoreAdj: 725, highOOMScoreAdj: 725}, + "burstable-container-2": {lowOOMScoreAdj: 850, highOOMScoreAdj: 850}, + "burstable-sidecar": {lowOOMScoreAdj: 850, highOOMScoreAdj: 850}, + }, + memoryCapacity: 4000000000, + podLevelResourcesFeatureEnabled: true, + sidecarContainersFeatureEnabled: true, + }, } for name, test := range oomTests { t.Run(name, func(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SidecarContainers, test.sidecarContainersFeatureEnabled) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, test.podLevelResourcesFeatureEnabled) listContainers := test.pod.Spec.InitContainers listContainers = append(listContainers, test.pod.Spec.Containers...) for _, container := range listContainers { diff --git a/pkg/quota/v1/evaluator/core/pods.go b/pkg/quota/v1/evaluator/core/pods.go index ac438685db0..8efdec6cc2b 100644 --- a/pkg/quota/v1/evaluator/core/pods.go +++ b/pkg/quota/v1/evaluator/core/pods.go @@ -122,6 +122,18 @@ func (p *podEvaluator) Constraints(required []corev1.ResourceName, item runtime. return err } + // As mentioned in the subsequent comment, the older versions required explicit + // resource requests for CPU & memory for each container if resource quotas were + // enabled for these resources. This was a design flaw as resource validation is + // coupled with quota enforcement. With pod-level resources + // feature, container-level resources are not mandatory. Hence the check for + // missing container requests, for CPU/memory resources that have quotas set, + // is skipped when pod-level resources feature is enabled and resources are set + // at pod level. + if feature.DefaultFeatureGate.Enabled(features.PodLevelResources) && resourcehelper.IsPodLevelResourcesSet(pod) { + return nil + } + // BACKWARD COMPATIBILITY REQUIREMENT: if we quota cpu or memory, then each container // must make an explicit request for the resource. this was a mistake. it coupled // validation with resource counting, but we did this before QoS was even defined. @@ -367,6 +379,8 @@ func PodUsageFunc(obj runtime.Object, clock clock.Clock) (corev1.ResourceList, e opts := resourcehelper.PodResourcesOptions{ UseStatusResources: feature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !feature.DefaultFeatureGate.Enabled(features.PodLevelResources), } requests := resourcehelper.PodRequests(pod, opts) limits := resourcehelper.PodLimits(pod, opts) diff --git a/pkg/quota/v1/evaluator/core/pods_test.go b/pkg/quota/v1/evaluator/core/pods_test.go index 11de6569212..15f9945f29c 100644 --- a/pkg/quota/v1/evaluator/core/pods_test.go +++ b/pkg/quota/v1/evaluator/core/pods_test.go @@ -42,9 +42,10 @@ import ( func TestPodConstraintsFunc(t *testing.T) { testCases := map[string]struct { - pod *api.Pod - required []corev1.ResourceName - err string + pod *api.Pod + required []corev1.ResourceName + err string + podLevelResourcesEnabled bool }{ "init container resource missing": { pod: &api.Pod{ @@ -133,9 +134,30 @@ func TestPodConstraintsFunc(t *testing.T) { required: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceCPU}, err: `must specify cpu for: bar,foo; memory for: bar,foo`, }, + "pod-level resource set, container-level required resources missing": { + pod: &api.Pod{ + Spec: api.PodSpec{ + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("1m")}, + }, + Containers: []api.Container{{ + Name: "foo", + Resources: api.ResourceRequirements{}, + }, { + Name: "bar", + Resources: api.ResourceRequirements{}, + }}, + }, + }, + required: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceCPU}, + podLevelResourcesEnabled: true, + err: ``, + }, } evaluator := NewPodEvaluator(nil, clock.RealClock{}) for testName, test := range testCases { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, test.podLevelResourcesEnabled) + err := evaluator.Constraints(test.required, test.pod) switch { case err != nil && len(test.err) == 0, @@ -158,8 +180,9 @@ func TestPodEvaluatorUsage(t *testing.T) { deletionTimestampNotPastGracePeriod := metav1.NewTime(fakeClock.Now()) testCases := map[string]struct { - pod *api.Pod - usage corev1.ResourceList + pod *api.Pod + usage corev1.ResourceList + podLevelResourcesEnabled bool }{ "init container CPU": { pod: &api.Pod{ @@ -529,10 +552,74 @@ func TestPodEvaluatorUsage(t *testing.T) { generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, + "pod-level CPU": { + pod: &api.Pod{ + Spec: api.PodSpec{ + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("1m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("2m")}, + }, + }, + }, + podLevelResourcesEnabled: true, + usage: corev1.ResourceList{ + corev1.ResourceRequestsCPU: resource.MustParse("1m"), + corev1.ResourceLimitsCPU: resource.MustParse("2m"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceCPU: resource.MustParse("1m"), + generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), + }, + }, + "pod-level Memory": { + pod: &api.Pod{ + Spec: api.PodSpec{ + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceMemory: resource.MustParse("1Mi")}, + Limits: api.ResourceList{api.ResourceMemory: resource.MustParse("2Mi")}, + }, + }, + }, + podLevelResourcesEnabled: true, + usage: corev1.ResourceList{ + corev1.ResourceRequestsMemory: resource.MustParse("1Mi"), + corev1.ResourceLimitsMemory: resource.MustParse("2Mi"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Mi"), + generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), + }, + }, + "pod-level memory with container-level ephemeral storage": { + pod: &api.Pod{ + Spec: api.PodSpec{ + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceMemory: resource.MustParse("1Mi")}, + Limits: api.ResourceList{api.ResourceMemory: resource.MustParse("2Mi")}, + }, + Containers: []api.Container{{ + Resources: api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceEphemeralStorage: resource.MustParse("32Mi")}, + Limits: api.ResourceList{api.ResourceEphemeralStorage: resource.MustParse("64Mi")}, + }, + }}, + }, + }, + podLevelResourcesEnabled: true, + usage: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("32Mi"), + corev1.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), + corev1.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceRequestsMemory: resource.MustParse("1Mi"), + corev1.ResourceLimitsMemory: resource.MustParse("2Mi"), + corev1.ResourceMemory: resource.MustParse("1Mi"), + generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), + }, + }, } t.Parallel() for testName, testCase := range testCases { t.Run(testName, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, testCase.podLevelResourcesEnabled) actual, err := evaluator.Usage(testCase.pod) if err != nil { t.Error(err) diff --git a/pkg/scheduler/eventhandlers.go b/pkg/scheduler/eventhandlers.go index b3212174cbe..3b009d76fb6 100644 --- a/pkg/scheduler/eventhandlers.go +++ b/pkg/scheduler/eventhandlers.go @@ -623,7 +623,9 @@ func preCheckForNode(nodeInfo *framework.NodeInfo) queue.PreEnqueueCheck { // returns all failures. func AdmissionCheck(pod *v1.Pod, nodeInfo *framework.NodeInfo, includeAllFailures bool) []AdmissionResult { var admissionResults []AdmissionResult - insufficientResources := noderesources.Fits(pod, nodeInfo) + insufficientResources := noderesources.Fits(pod, nodeInfo, noderesources.ResourceRequestsOptions{ + EnablePodLevelResources: utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources), + }) if len(insufficientResources) != 0 { for i := range insufficientResources { admissionResults = append(admissionResults, AdmissionResult{InsufficientResource: &insufficientResources[i]}) diff --git a/pkg/scheduler/framework/plugins/feature/feature.go b/pkg/scheduler/framework/plugins/feature/feature.go index ff1920cca57..d4c5cc62d60 100644 --- a/pkg/scheduler/framework/plugins/feature/feature.go +++ b/pkg/scheduler/framework/plugins/feature/feature.go @@ -29,4 +29,5 @@ type Features struct { EnableSidecarContainers bool EnableSchedulingQueueHint bool EnableAsyncPreemption bool + EnablePodLevelResources bool } diff --git a/pkg/scheduler/framework/plugins/noderesources/fit.go b/pkg/scheduler/framework/plugins/noderesources/fit.go index 0a7f387a42d..cd464dd5121 100644 --- a/pkg/scheduler/framework/plugins/noderesources/fit.go +++ b/pkg/scheduler/framework/plugins/noderesources/fit.go @@ -90,6 +90,7 @@ type Fit struct { enableInPlacePodVerticalScaling bool enableSidecarContainers bool enableSchedulingQueueHint bool + enablePodLevelResources bool handle framework.Handle resourceAllocationScorer } @@ -176,10 +177,15 @@ func NewFit(_ context.Context, plArgs runtime.Object, h framework.Handle, fts fe enableSidecarContainers: fts.EnableSidecarContainers, enableSchedulingQueueHint: fts.EnableSchedulingQueueHint, handle: h, + enablePodLevelResources: fts.EnablePodLevelResources, resourceAllocationScorer: *scorePlugin(args), }, nil } +type ResourceRequestsOptions struct { + EnablePodLevelResources bool +} + // computePodResourceRequest returns a framework.Resource that covers the largest // width in each resource dimension. Because init-containers run sequentially, we collect // the max in each dimension iteratively. In contrast, we sum the resource vectors for @@ -207,9 +213,14 @@ func NewFit(_ context.Context, plArgs runtime.Object, h framework.Handle, fts fe // Memory: 1G // // Result: CPU: 3, Memory: 3G -func computePodResourceRequest(pod *v1.Pod) *preFilterState { +// TODO(ndixita): modify computePodResourceRequest to accept opts of type +// ResourceRequestOptions as the second parameter. +func computePodResourceRequest(pod *v1.Pod, opts ResourceRequestsOptions) *preFilterState { // pod hasn't scheduled yet so we don't need to worry about InPlacePodVerticalScalingEnabled - reqs := resource.PodRequests(pod, resource.PodResourcesOptions{}) + reqs := resource.PodRequests(pod, resource.PodResourcesOptions{ + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !opts.EnablePodLevelResources, + }) result := &preFilterState{} result.SetMaxResource(reqs) return result @@ -225,7 +236,7 @@ func (f *Fit) PreFilter(ctx context.Context, cycleState *framework.CycleState, p // and the older (before v1.28) kubelet, make the Pod unschedulable. return nil, framework.NewStatus(framework.UnschedulableAndUnresolvable, "Pod has a restartable init container and the SidecarContainers feature is disabled") } - cycleState.Write(preFilterStateKey, computePodResourceRequest(pod)) + cycleState.Write(preFilterStateKey, computePodResourceRequest(pod, ResourceRequestsOptions{EnablePodLevelResources: f.enablePodLevelResources})) return nil, nil } @@ -370,7 +381,7 @@ func (f *Fit) isSchedulableAfterNodeChange(logger klog.Logger, pod *v1.Pod, oldO return framework.Queue, err } // Leaving in the queue, since the pod won't fit into the modified node anyway. - if !isFit(pod, modifiedNode) { + if !isFit(pod, modifiedNode, ResourceRequestsOptions{EnablePodLevelResources: f.enablePodLevelResources}) { logger.V(5).Info("node was created or updated, but it doesn't have enough resource(s) to accommodate this pod", "pod", klog.KObj(pod), "node", klog.KObj(modifiedNode)) return framework.QueueSkip, nil } @@ -380,7 +391,7 @@ func (f *Fit) isSchedulableAfterNodeChange(logger klog.Logger, pod *v1.Pod, oldO return framework.Queue, nil } // The pod will fit, but since there was no increase in available resources, the change won't make the pod schedulable. - if !haveAnyRequestedResourcesIncreased(pod, originalNode, modifiedNode) { + if !haveAnyRequestedResourcesIncreased(pod, originalNode, modifiedNode, ResourceRequestsOptions{EnablePodLevelResources: f.enablePodLevelResources}) { logger.V(5).Info("node was updated, but haven't changed the pod's resource requestments fit assessment", "pod", klog.KObj(pod), "node", klog.KObj(modifiedNode)) return framework.QueueSkip, nil } @@ -390,8 +401,8 @@ func (f *Fit) isSchedulableAfterNodeChange(logger klog.Logger, pod *v1.Pod, oldO } // haveAnyRequestedResourcesIncreased returns true if any of the resources requested by the pod have increased or if allowed pod number increased. -func haveAnyRequestedResourcesIncreased(pod *v1.Pod, originalNode, modifiedNode *v1.Node) bool { - podRequest := computePodResourceRequest(pod) +func haveAnyRequestedResourcesIncreased(pod *v1.Pod, originalNode, modifiedNode *v1.Node, opts ResourceRequestsOptions) bool { + podRequest := computePodResourceRequest(pod, opts) originalNodeInfo := framework.NewNodeInfo() originalNodeInfo.SetNode(originalNode) modifiedNodeInfo := framework.NewNodeInfo() @@ -429,13 +440,13 @@ func haveAnyRequestedResourcesIncreased(pod *v1.Pod, originalNode, modifiedNode // isFit checks if the pod fits the node. If the node is nil, it returns false. // It constructs a fake NodeInfo object for the node and checks if the pod fits the node. -func isFit(pod *v1.Pod, node *v1.Node) bool { +func isFit(pod *v1.Pod, node *v1.Node, opts ResourceRequestsOptions) bool { if node == nil { return false } nodeInfo := framework.NewNodeInfo() nodeInfo.SetNode(node) - return len(Fits(pod, nodeInfo)) == 0 + return len(Fits(pod, nodeInfo, opts)) == 0 } // Filter invoked at the filter extension point. @@ -481,8 +492,8 @@ type InsufficientResource struct { } // Fits checks if node have enough resources to host the pod. -func Fits(pod *v1.Pod, nodeInfo *framework.NodeInfo) []InsufficientResource { - return fitsRequest(computePodResourceRequest(pod), nodeInfo, nil, nil) +func Fits(pod *v1.Pod, nodeInfo *framework.NodeInfo, opts ResourceRequestsOptions) []InsufficientResource { + return fitsRequest(computePodResourceRequest(pod, opts), nodeInfo, nil, nil) } func fitsRequest(podRequest *preFilterState, nodeInfo *framework.NodeInfo, ignoredExtendedResources, ignoredResourceGroups sets.Set[string]) []InsufficientResource { diff --git a/pkg/scheduler/framework/plugins/noderesources/fit_test.go b/pkg/scheduler/framework/plugins/noderesources/fit_test.go index e1181288704..d118e6f2503 100644 --- a/pkg/scheduler/framework/plugins/noderesources/fit_test.go +++ b/pkg/scheduler/framework/plugins/noderesources/fit_test.go @@ -119,12 +119,18 @@ var defaultScoringStrategy = &config.ScoringStrategy{ }, } +func newPodLevelResourcesPod(pod *v1.Pod, podResources v1.ResourceRequirements) *v1.Pod { + pod.Spec.Resources = &podResources + return pod +} + func TestEnoughRequests(t *testing.T) { enoughPodsTests := []struct { pod *v1.Pod nodeInfo *framework.NodeInfo name string args config.NodeResourcesFitArgs + podLevelResourcesEnabled bool wantInsufficientResources []InsufficientResource wantStatus *framework.Status }{ @@ -478,6 +484,7 @@ func TestEnoughRequests(t *testing.T) { wantInsufficientResources: []InsufficientResource{}, }, { + podLevelResourcesEnabled: true, pod: newResourcePod( framework.Resource{ ScalarResources: map[v1.ResourceName]int64{ @@ -488,10 +495,74 @@ func TestEnoughRequests(t *testing.T) { name: "skip checking resource request with quantity zero", wantInsufficientResources: []InsufficientResource{}, }, + { + podLevelResourcesEnabled: true, + pod: newPodLevelResourcesPod( + newResourcePod(framework.Resource{MilliCPU: 1, Memory: 1}), + v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("1m"), v1.ResourceMemory: resource.MustParse("2")}, + }, + ), + nodeInfo: framework.NewNodeInfo( + newResourcePod(framework.Resource{MilliCPU: 5, Memory: 5})), + name: "both pod-level and container-level resources fit", + wantInsufficientResources: []InsufficientResource{}, + }, + { + podLevelResourcesEnabled: true, + pod: newPodLevelResourcesPod( + newResourcePod(framework.Resource{MilliCPU: 1, Memory: 1}), + v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("7m"), v1.ResourceMemory: resource.MustParse("2")}, + }, + ), + nodeInfo: framework.NewNodeInfo( + newResourcePod(framework.Resource{MilliCPU: 5, Memory: 5})), + name: "pod-level cpu resource not fit", + wantStatus: framework.NewStatus(framework.Unschedulable, getErrReason(v1.ResourceCPU)), + wantInsufficientResources: []InsufficientResource{{ + ResourceName: v1.ResourceCPU, Reason: getErrReason(v1.ResourceCPU), Requested: 7, Used: 5, Capacity: 10}, + }, + }, + { + podLevelResourcesEnabled: true, + pod: newPodLevelResourcesPod( + newResourcePod(framework.Resource{MilliCPU: 1, Memory: 1}), + v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("3m"), v1.ResourceMemory: resource.MustParse("2")}, + }, + ), + nodeInfo: framework.NewNodeInfo( + newResourcePod(framework.Resource{MilliCPU: 5, Memory: 19})), + name: "pod-level memory resource not fit", + wantStatus: framework.NewStatus(framework.Unschedulable, getErrReason(v1.ResourceMemory)), + wantInsufficientResources: []InsufficientResource{{ + ResourceName: v1.ResourceMemory, Reason: getErrReason(v1.ResourceMemory), Requested: 2, Used: 19, Capacity: 20}, + }, + }, + { + podLevelResourcesEnabled: true, + pod: newResourceInitPod(newPodLevelResourcesPod( + newResourcePod(framework.Resource{MilliCPU: 1, Memory: 1}), + v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("3m"), v1.ResourceMemory: resource.MustParse("2")}, + }, + ), + framework.Resource{MilliCPU: 1, Memory: 1}, + ), + nodeInfo: framework.NewNodeInfo( + newResourcePod(framework.Resource{MilliCPU: 5, Memory: 19})), + name: "one pod-level cpu resource fits and all init and non-init containers resources fit", + wantStatus: framework.NewStatus(framework.Unschedulable, getErrReason(v1.ResourceMemory)), + wantInsufficientResources: []InsufficientResource{{ + ResourceName: v1.ResourceMemory, Reason: getErrReason(v1.ResourceMemory), Requested: 2, Used: 19, Capacity: 20}, + }, + }, } for _, test := range enoughPodsTests { t.Run(test.name, func(t *testing.T) { + node := v1.Node{Status: v1.NodeStatus{Capacity: makeResources(10, 20, 32, 5, 20, 5), Allocatable: makeAllocatableResources(10, 20, 32, 5, 20, 5)}} test.nodeInfo.SetNode(&node) @@ -502,7 +573,7 @@ func TestEnoughRequests(t *testing.T) { _, ctx := ktesting.NewTestContext(t) ctx, cancel := context.WithCancel(ctx) defer cancel() - p, err := NewFit(ctx, &test.args, nil, plfeature.Features{}) + p, err := NewFit(ctx, &test.args, nil, plfeature.Features{EnablePodLevelResources: test.podLevelResourcesEnabled}) if err != nil { t.Fatal(err) } @@ -517,7 +588,7 @@ func TestEnoughRequests(t *testing.T) { t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus) } - gotInsufficientResources := fitsRequest(computePodResourceRequest(test.pod), test.nodeInfo, p.(*Fit).ignoredResources, p.(*Fit).ignoredResourceGroups) + gotInsufficientResources := fitsRequest(computePodResourceRequest(test.pod, ResourceRequestsOptions{EnablePodLevelResources: test.podLevelResourcesEnabled}), test.nodeInfo, p.(*Fit).ignoredResources, p.(*Fit).ignoredResourceGroups) if !reflect.DeepEqual(gotInsufficientResources, test.wantInsufficientResources) { t.Errorf("insufficient resources do not match: %+v, want: %v", gotInsufficientResources, test.wantInsufficientResources) } @@ -1434,9 +1505,10 @@ func Test_isSchedulableAfterNodeChange(t *testing.T) { func TestIsFit(t *testing.T) { testCases := map[string]struct { - pod *v1.Pod - node *v1.Node - expected bool + pod *v1.Pod + node *v1.Node + podLevelResourcesEnabled bool + expected bool }{ "nil node": { pod: &v1.Pod{}, @@ -1452,11 +1524,26 @@ func TestIsFit(t *testing.T) { node: st.MakeNode().Capacity(map[v1.ResourceName]string{v1.ResourceCPU: "2"}).Obj(), expected: true, }, + "insufficient pod-level resource": { + pod: st.MakePod().Resources( + v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("2")}}, + ).Obj(), + node: st.MakeNode().Capacity(map[v1.ResourceName]string{v1.ResourceCPU: "1"}).Obj(), + podLevelResourcesEnabled: true, + expected: false, + }, + "sufficient pod-level resource": { + pod: st.MakePod().Resources( + v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("2")}}, + ).Obj(), + node: st.MakeNode().Capacity(map[v1.ResourceName]string{v1.ResourceCPU: "2"}).Obj(), + expected: true, + }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { - if got := isFit(tc.pod, tc.node); got != tc.expected { + if got := isFit(tc.pod, tc.node, ResourceRequestsOptions{tc.podLevelResourcesEnabled}); got != tc.expected { t.Errorf("expected: %v, got: %v", tc.expected, got) } }) @@ -1589,7 +1676,7 @@ func TestHaveAnyRequestedResourcesIncreased(t *testing.T) { } for name, tc := range testCases { t.Run(name, func(t *testing.T) { - if got := haveAnyRequestedResourcesIncreased(tc.pod, tc.originalNode, tc.modifiedNode); got != tc.expected { + if got := haveAnyRequestedResourcesIncreased(tc.pod, tc.originalNode, tc.modifiedNode, ResourceRequestsOptions{}); got != tc.expected { t.Errorf("expected: %v, got: %v", tc.expected, got) } }) diff --git a/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go b/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go index 118fb7e07c1..e8526629485 100644 --- a/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go +++ b/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go @@ -119,7 +119,10 @@ func (r *resourceAllocationScorer) calculatePodResourceRequest(pod *v1.Pod, reso opts := resourcehelper.PodResourcesOptions{ UseStatusResources: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources), } + if !r.useRequested { opts.NonMissingContainerRequests = v1.ResourceList{ v1.ResourceCPU: *resource.NewMilliQuantity(schedutil.DefaultMilliCPURequest, resource.DecimalSI), diff --git a/pkg/scheduler/framework/plugins/registry.go b/pkg/scheduler/framework/plugins/registry.go index 009b6c89967..ae50798a3d6 100644 --- a/pkg/scheduler/framework/plugins/registry.go +++ b/pkg/scheduler/framework/plugins/registry.go @@ -55,6 +55,7 @@ func NewInTreeRegistry() runtime.Registry { EnableSidecarContainers: feature.DefaultFeatureGate.Enabled(features.SidecarContainers), EnableSchedulingQueueHint: feature.DefaultFeatureGate.Enabled(features.SchedulerQueueingHints), EnableAsyncPreemption: feature.DefaultFeatureGate.Enabled(features.SchedulerAsyncPreemption), + EnablePodLevelResources: feature.DefaultFeatureGate.Enabled(features.PodLevelResources), } registry := runtime.Registry{ diff --git a/pkg/scheduler/framework/types.go b/pkg/scheduler/framework/types.go index 4e63acf95c4..ac671899771 100644 --- a/pkg/scheduler/framework/types.go +++ b/pkg/scheduler/framework/types.go @@ -1052,19 +1052,74 @@ func (n *NodeInfo) update(pod *v1.Pod, sign int64) { n.Generation = nextGeneration() } +// getNonMissingContainerRequests returns the default non-zero CPU and memory +// requests for a container that the scheduler uses when container-level and +// pod-level requests are not set for a resource. It returns a ResourceList that +// includes these default non-zero requests, which are essential for the +// scheduler to function correctly. +// The method's behavior depends on whether pod-level resources are set or not: +// 1. When the pod level resources are not set, the method returns a ResourceList +// with the following defaults: +// - CPU: schedutil.DefaultMilliCPURequest +// - Memory: schedutil.DefaultMemoryRequest +// +// These defaults ensure that each container has a minimum resource request, +// allowing the scheduler to aggregate these requests and find a suitable node +// for the pod. +// +// 2. When the pod level resources are set, if a CPU or memory request is +// missing at the container-level *and* at the pod-level, the corresponding +// default value (schedutil.DefaultMilliCPURequest or schedutil.DefaultMemoryRequest) +// is included in the returned ResourceList. +// Note that these default values are not set in the Pod object itself, they are only used +// by the scheduler during node selection. +func getNonMissingContainerRequests(requests v1.ResourceList, podLevelResourcesSet bool) v1.ResourceList { + if !podLevelResourcesSet { + return v1.ResourceList{ + v1.ResourceCPU: *resource.NewMilliQuantity(schedutil.DefaultMilliCPURequest, resource.DecimalSI), + v1.ResourceMemory: *resource.NewQuantity(schedutil.DefaultMemoryRequest, resource.DecimalSI), + } + } + + nonMissingContainerRequests := make(v1.ResourceList, 2) + // DefaultMilliCPURequest serves as the fallback value when both + // pod-level and container-level CPU requests are not set. + // Note that the apiserver defaulting logic will propagate a non-zero + // container-level CPU request to the pod level if a pod-level request + // is not explicitly set. + if _, exists := requests[v1.ResourceCPU]; !exists { + nonMissingContainerRequests[v1.ResourceCPU] = *resource.NewMilliQuantity(schedutil.DefaultMilliCPURequest, resource.DecimalSI) + } + + // DefaultMemoryRequest serves as the fallback value when both + // pod-level and container-level CPU requests are unspecified. + // Note that the apiserver defaulting logic will propagate a non-zero + // container-level memory request to the pod level if a pod-level request + // is not explicitly set. + if _, exists := requests[v1.ResourceMemory]; !exists { + nonMissingContainerRequests[v1.ResourceMemory] = *resource.NewQuantity(schedutil.DefaultMemoryRequest, resource.DecimalSI) + } + return nonMissingContainerRequests + +} + func calculateResource(pod *v1.Pod) (Resource, int64, int64) { requests := resourcehelper.PodRequests(pod, resourcehelper.PodResourcesOptions{ UseStatusResources: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources), }) - - non0Requests := resourcehelper.PodRequests(pod, resourcehelper.PodResourcesOptions{ - UseStatusResources: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), - NonMissingContainerRequests: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: *resource.NewMilliQuantity(schedutil.DefaultMilliCPURequest, resource.DecimalSI), - v1.ResourceMemory: *resource.NewQuantity(schedutil.DefaultMemoryRequest, resource.DecimalSI), - }, - }) - + isPodLevelResourcesSet := utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) && resourcehelper.IsPodLevelRequestsSet(pod) + nonMissingContainerRequests := getNonMissingContainerRequests(requests, isPodLevelResourcesSet) + non0Requests := requests + if len(nonMissingContainerRequests) > 0 { + non0Requests = resourcehelper.PodRequests(pod, resourcehelper.PodResourcesOptions{ + UseStatusResources: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), + // SkipPodLevelResources is set to false when PodLevelResources feature is enabled. + SkipPodLevelResources: !utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources), + NonMissingContainerRequests: nonMissingContainerRequests, + }) + } non0CPU := non0Requests[v1.ResourceCPU] non0Mem := non0Requests[v1.ResourceMemory] diff --git a/pkg/scheduler/framework/types_test.go b/pkg/scheduler/framework/types_test.go index 8972aca1cdb..2120c840fc3 100644 --- a/pkg/scheduler/framework/types_test.go +++ b/pkg/scheduler/framework/types_test.go @@ -34,6 +34,7 @@ import ( "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/features" st "k8s.io/kubernetes/pkg/scheduler/testing" + schedutil "k8s.io/kubernetes/pkg/scheduler/util" "k8s.io/kubernetes/test/utils/ktesting" "k8s.io/kubernetes/test/utils/ktesting/initoption" ) @@ -1513,12 +1514,264 @@ func TestFitError_Error(t *testing.T) { } } +var ( + cpu500m = resource.MustParse("500m") + mem500M = resource.MustParse("500Mi") + cpu700m = resource.MustParse("700m") + mem800M = resource.MustParse("800Mi") + cpu1200m = resource.MustParse("1200m") + mem1200M = resource.MustParse("1200Mi") + restartAlways = v1.ContainerRestartPolicyAlways +) + +func TestCalculateResources(t *testing.T) { + testCases := []struct { + name string + containers []v1.Container + podResources *v1.ResourceRequirements + podLevelResourcesEnabled bool + expectedResource Resource + expectedNon0CPU int64 + expectedNon0Mem int64 + initContainers []v1.Container + }{ + { + name: "requestless container", + containers: []v1.Container{{}}, + expectedResource: Resource{}, + expectedNon0CPU: schedutil.DefaultMilliCPURequest, + expectedNon0Mem: schedutil.DefaultMemoryRequest, + }, + { + name: "1X container with requests", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + v1.ResourceMemory: mem500M, + }, + }, + }, + }, + expectedResource: Resource{ + MilliCPU: cpu500m.MilliValue(), + Memory: mem500M.Value(), + }, + expectedNon0CPU: cpu500m.MilliValue(), + expectedNon0Mem: mem500M.Value(), + }, + { + name: "2X container with requests", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + v1.ResourceMemory: mem500M, + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu700m, + v1.ResourceMemory: mem800M, + }, + }, + }, + }, + expectedResource: Resource{ + MilliCPU: cpu500m.MilliValue() + cpu700m.MilliValue(), + Memory: mem500M.Value() + mem800M.Value(), + }, + expectedNon0CPU: cpu500m.MilliValue() + cpu700m.MilliValue(), + expectedNon0Mem: mem500M.Value() + mem800M.Value(), + }, + { + name: "1X container and 1X init container with pod-level requests", + podLevelResourcesEnabled: true, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + v1.ResourceMemory: mem500M, + }, + }, + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + v1.ResourceMemory: mem500M, + }, + }, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu1200m, + v1.ResourceMemory: mem1200M, + }, + }, + expectedResource: Resource{ + MilliCPU: cpu1200m.MilliValue(), + Memory: mem1200M.Value(), + }, + expectedNon0CPU: cpu1200m.MilliValue(), + expectedNon0Mem: mem1200M.Value(), + }, + { + name: "1X container and 1X sidecar container with pod-level requests", + podLevelResourcesEnabled: true, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + v1.ResourceMemory: mem500M, + }, + }, + RestartPolicy: &restartAlways, + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + v1.ResourceMemory: mem500M, + }, + }, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu1200m, + v1.ResourceMemory: mem1200M, + }, + }, + expectedResource: Resource{ + MilliCPU: cpu1200m.MilliValue(), + Memory: mem1200M.Value(), + }, + expectedNon0CPU: cpu1200m.MilliValue(), + expectedNon0Mem: mem1200M.Value(), + }, + { + name: "1X container with pod-level memory requests", + podLevelResourcesEnabled: true, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{}, + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{}, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: mem1200M, + }, + }, + expectedResource: Resource{ + Memory: mem1200M.Value(), + }, + expectedNon0CPU: schedutil.DefaultMilliCPURequest, + expectedNon0Mem: mem1200M.Value(), + }, + { + name: "1X container with pod-level cpu requests", + podLevelResourcesEnabled: true, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{}, + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{}, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + }, + }, + expectedResource: Resource{ + MilliCPU: cpu500m.MilliValue(), + }, + expectedNon0CPU: cpu500m.MilliValue(), + expectedNon0Mem: schedutil.DefaultMemoryRequest, + }, + { + name: "1X container unsupported resources and pod-level supported resources", + podLevelResourcesEnabled: true, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceEphemeralStorage: mem500M, + }, + }, + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceEphemeralStorage: mem800M, + }, + }, + }, + }, + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: cpu500m, + }, + }, + expectedResource: Resource{ + MilliCPU: cpu500m.MilliValue(), + EphemeralStorage: mem800M.Value(), + }, + expectedNon0CPU: cpu500m.MilliValue(), + expectedNon0Mem: schedutil.DefaultMemoryRequest, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, tc.podLevelResourcesEnabled) + pod := &v1.Pod{ + Spec: v1.PodSpec{ + Resources: tc.podResources, + Containers: tc.containers, + InitContainers: tc.initContainers, + }, + } + res, non0CPU, non0Mem := calculateResource(pod) + if !reflect.DeepEqual(res, tc.expectedResource) { + t.Errorf("Test: %s expected resource: %+v, got: %+v", tc.name, tc.expectedResource, res) + } + + if non0CPU != tc.expectedNon0CPU { + t.Errorf("Test: %s expected non0CPU: %d, got: %d", tc.name, tc.expectedNon0CPU, non0CPU) + } + + if non0Mem != tc.expectedNon0Mem { + t.Errorf("Test: %s expected non0Mem: %d, got: %d", tc.name, tc.expectedNon0Mem, non0Mem) + } + }) + } +} + func TestCalculatePodResourcesWithResize(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true) - cpu500m := resource.MustParse("500m") - mem500M := resource.MustParse("500Mi") - cpu700m := resource.MustParse("700m") - mem800M := resource.MustParse("800Mi") testpod := v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "pod_resize_test", diff --git a/pkg/scheduler/testing/wrappers.go b/pkg/scheduler/testing/wrappers.go index a38b272100b..96970b84486 100644 --- a/pkg/scheduler/testing/wrappers.go +++ b/pkg/scheduler/testing/wrappers.go @@ -334,6 +334,12 @@ func (p *PodWrapper) Namespace(s string) *PodWrapper { return p } +// Resources sets requests and limits at pod-level. +func (p *PodWrapper) Resources(resources v1.ResourceRequirements) *PodWrapper { + p.Spec.Resources = &resources + return p +} + // OwnerReference updates the owning controller of the pod. func (p *PodWrapper) OwnerReference(name string, gvk schema.GroupVersionKind) *PodWrapper { p.OwnerReferences = []metav1.OwnerReference{ diff --git a/plugin/pkg/admission/limitranger/admission.go b/plugin/pkg/admission/limitranger/admission.go index db02fbd7dd4..55a65b056bb 100644 --- a/plugin/pkg/admission/limitranger/admission.go +++ b/plugin/pkg/admission/limitranger/admission.go @@ -32,14 +32,17 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/admission" genericadmissioninitailizer "k8s.io/apiserver/pkg/admission/initializer" + "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/utils/lru" api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/features" ) const ( @@ -528,8 +531,11 @@ func PodValidateLimitFunc(limitRange *corev1.LimitRange, pod *api.Pod) error { // enforce pod limits on init containers if limitType == corev1.LimitTypePod { - podRequests := podRequests(pod) - podLimits := podLimits(pod) + opts := podResourcesOptions{ + PodLevelResourcesEnabled: feature.DefaultFeatureGate.Enabled(features.PodLevelResources), + } + podRequests := podRequests(pod, opts) + podLimits := podLimits(pod, opts) for k, v := range limit.Min { if err := minConstraint(string(limitType), string(k), v, podRequests, podLimits); err != nil { errs = append(errs, err) @@ -550,13 +556,22 @@ func PodValidateLimitFunc(limitRange *corev1.LimitRange, pod *api.Pod) error { return utilerrors.NewAggregate(errs) } +type podResourcesOptions struct { + // PodLevelResourcesEnabled indicates that the PodLevelResources feature gate is + // enabled. + PodLevelResourcesEnabled bool +} + // podRequests is a simplified version of pkg/api/v1/resource/PodRequests that operates against the core version of // pod. Any changes to that calculation should be reflected here. // NOTE: We do not want to check status resources here, only the spec. This is equivalent to setting // UseStatusResources=false in the common helper. // TODO: Maybe we can consider doing a partial conversion of the pod to a v1 // type and then using the pkg/api/v1/resource/PodRequests. -func podRequests(pod *api.Pod) api.ResourceList { +// TODO(ndixita): PodRequests method exists in +// staging/src/k8s.io/component-helpers/resource/helpers.go. Refactor the code to +// avoid duplicating podRequests method. +func podRequests(pod *api.Pod, opts podResourcesOptions) api.ResourceList { reqs := api.ResourceList{} for _, container := range pod.Spec.Containers { @@ -589,6 +604,19 @@ func podRequests(pod *api.Pod) api.ResourceList { } maxResourceList(reqs, initContainerReqs) + + // If PodLevelResources feature is enabled and resources are set at pod-level, + // override aggregated container requests of resources supported by pod-level + // resources with quantities specified at pod-level. + if opts.PodLevelResourcesEnabled && pod.Spec.Resources != nil { + for resourceName, quantity := range pod.Spec.Resources.Requests { + if isSupportedPodLevelResource(resourceName) { + // override with pod-level resource requests + reqs[resourceName] = quantity + } + } + } + return reqs } @@ -598,7 +626,10 @@ func podRequests(pod *api.Pod) api.ResourceList { // UseStatusResources=false in the common helper. // TODO: Maybe we can consider doing a partial conversion of the pod to a v1 // type and then using the pkg/api/v1/resource/PodLimits. -func podLimits(pod *api.Pod) api.ResourceList { +// TODO(ndixita): PodLimits method exists in +// staging/src/k8s.io/component-helpers/resource/helpers.go. Refactor the code to +// avoid duplicating podLimits method. +func podLimits(pod *api.Pod, opts podResourcesOptions) api.ResourceList { limits := api.ResourceList{} for _, container := range pod.Spec.Containers { @@ -628,9 +659,35 @@ func podLimits(pod *api.Pod) api.ResourceList { maxResourceList(limits, initContainerLimits) + // If PodLevelResources feature is enabled and resources are set at pod-level, + // override aggregated container limits of resources supported by pod-level + // resources with quantities specified at pod-level. + if opts.PodLevelResourcesEnabled && pod.Spec.Resources != nil { + for resourceName, quantity := range pod.Spec.Resources.Limits { + if isSupportedPodLevelResource(resourceName) { + // override with pod-level resource limits + limits[resourceName] = quantity + } + } + } + return limits } +var supportedPodLevelResources = sets.New(api.ResourceCPU, api.ResourceMemory) + +// isSupportedPodLevelResources checks if a given resource is supported by pod-level +// resource management through the PodLevelResources feature. Returns true if +// the resource is supported. +// isSupportedPodLevelResource method exists in +// staging/src/k8s.io/component-helpers/resource/helpers.go. +// isSupportedPodLevelResource is added here to avoid conversion of v1. +// Pod to api.Pod. +// TODO(ndixita): Find alternatives to avoid duplicating the code. +func isSupportedPodLevelResource(name api.ResourceName) bool { + return supportedPodLevelResources.Has(name) +} + // addResourceList adds the resources in newList to list. func addResourceList(list, newList api.ResourceList) { for name, quantity := range newList { diff --git a/plugin/pkg/admission/limitranger/admission_test.go b/plugin/pkg/admission/limitranger/admission_test.go index d8c4a2d3d1e..17fc3211af3 100644 --- a/plugin/pkg/admission/limitranger/admission_test.go +++ b/plugin/pkg/admission/limitranger/admission_test.go @@ -158,6 +158,12 @@ func validLimitRangeNoDefaults() corev1.LimitRange { return externalLimitRange } +func validPodWithPodLevelResources(name string, numContainers int, containerResources api.ResourceRequirements, podResources api.ResourceRequirements) api.Pod { + pod := validPod(name, numContainers, containerResources) + pod.Spec.Resources = &podResources + return pod +} + func validPod(name string, numContainers int, resources api.ResourceRequirements) api.Pod { pod := api.Pod{ ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"}, @@ -280,8 +286,9 @@ func TestMergePodResourceRequirements(t *testing.T) { func TestPodLimitFunc(t *testing.T) { type testCase struct { - pod api.Pod - limitRange corev1.LimitRange + pod api.Pod + limitRange corev1.LimitRange + podLevelResourcesEnabled bool } successCases := []testCase{ @@ -453,17 +460,42 @@ func TestPodLimitFunc(t *testing.T) { pod: validPod("pod-max-local-ephemeral-storage-ratio", 3, getResourceRequirements(getLocalStorageResourceList("300Mi"), getLocalStorageResourceList("450Mi"))), limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("2Gi"), api.ResourceList{}, api.ResourceList{}, getLocalStorageResourceList("1.5")), }, + { + pod: validPodWithPodLevelResources("pod-level-resources-with-min-max", 3, getResourceRequirements(getComputeResourceList("100m", "60Mi"), getComputeResourceList("200m", "100Mi")), + getResourceRequirements(getComputeResourceList("200m", "180Mi"), getComputeResourceList("400m", "200Mi")), + ), + limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("400m", "200Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + podLevelResourcesEnabled: true, + }, + { + pod: validPodWithPodLevelResources("pod-level-requests-with-min", 3, getResourceRequirements(getComputeResourceList("50m", "60Mi"), getComputeResourceList("", "")), + getResourceRequirements(getComputeResourceList("160m", "200Mi"), getComputeResourceList("", "")), + ), + limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("160m", "200Mi"), getComputeResourceList("", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + podLevelResourcesEnabled: true, + }, + { + pod: validPodWithPodLevelResources("pod-level-limits-with-max", 3, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("50m", "60Mi")), + getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("160m", "200Mi")), + ), + limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", ""), getComputeResourceList("160m", "200Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + podLevelResourcesEnabled: true, + }, } for i := range successCases { test := successCases[i] - err := PodMutateLimitFunc(&test.limitRange, &test.pod) - if err != nil { - t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err) - } - err = PodValidateLimitFunc(&test.limitRange, &test.pod) - if err != nil { - t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err) - } + t.Run(test.pod.Name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, test.podLevelResourcesEnabled) + err := PodMutateLimitFunc(&test.limitRange, &test.pod) + if err != nil { + t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err) + } + + err = PodValidateLimitFunc(&test.limitRange, &test.pod) + if err != nil { + t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err) + } + }) } errorCases := []testCase{ @@ -641,18 +673,41 @@ func TestPodLimitFunc(t *testing.T) { pod: withRestartableInitContainer(getComputeResourceList("1500m", ""), api.ResourceList{}, validPod("ctr-max-cpu-limit-restartable-init-container", 1, getResourceRequirements(getComputeResourceList("1000m", ""), getComputeResourceList("1500m", "")))), limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + }, { + pod: validPodWithPodLevelResources("pod-level-resources-exceeding-max", 3, getResourceRequirements(getComputeResourceList("100m", "60Mi"), getComputeResourceList("200m", "100Mi")), + getResourceRequirements(getComputeResourceList("200m", "180Mi"), getComputeResourceList("500m", "280Mi")), + ), + limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("400m", "200Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + podLevelResourcesEnabled: true, + }, + { + pod: validPodWithPodLevelResources("pod-level-requests-less-than-min", 3, getResourceRequirements(getComputeResourceList("50m", "60Mi"), getComputeResourceList("", "")), + getResourceRequirements(getComputeResourceList("100m", "200Mi"), getComputeResourceList("", "")), + ), + limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("160m", "200Mi"), getComputeResourceList("", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + podLevelResourcesEnabled: true, + }, + { + pod: validPodWithPodLevelResources("pod-level-limits-exceeding-max", 3, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("50m", "60Mi")), + getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("160m", "300Mi")), + ), + limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", ""), getComputeResourceList("160m", "200Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}), + podLevelResourcesEnabled: true, }, } for i := range errorCases { test := errorCases[i] - err := PodMutateLimitFunc(&test.limitRange, &test.pod) - if err != nil { - t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err) - } - err = PodValidateLimitFunc(&test.limitRange, &test.pod) - if err == nil { - t.Errorf("Expected error for pod: %s", test.pod.Name) - } + t.Run(test.pod.Name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, test.podLevelResourcesEnabled) + err := PodMutateLimitFunc(&test.limitRange, &test.pod) + if err != nil { + t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err) + } + err = PodValidateLimitFunc(&test.limitRange, &test.pod) + if err == nil { + t.Errorf("Expected error for pod: %s", test.pod.Name) + } + }) } } diff --git a/staging/publishing/import-restrictions.yaml b/staging/publishing/import-restrictions.yaml index ff825b3c237..989721473e7 100644 --- a/staging/publishing/import-restrictions.yaml +++ b/staging/publishing/import-restrictions.yaml @@ -13,6 +13,7 @@ - k8s.io/utils/ptr - k8s.io/utils/net - k8s.io/klog + - k8s.io/component-helpers/resource # the following are temporary and should go away. Think twice (or more) before adding anything here. # Main goal: pkg/apis should be as self-contained as possible. diff --git a/staging/src/k8s.io/api/core/v1/generated.pb.go b/staging/src/k8s.io/api/core/v1/generated.pb.go index 84fb2c61860..9d466c6d79d 100644 --- a/staging/src/k8s.io/api/core/v1/generated.pb.go +++ b/staging/src/k8s.io/api/core/v1/generated.pb.go @@ -6758,1014 +6758,1015 @@ func init() { } var fileDescriptor_6c07b07c062484ab = []byte{ - // 16099 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x7b, 0x90, 0x5c, 0xd9, - 0x59, 0x18, 0xee, 0xdb, 0x3d, 0xcf, 0x6f, 0xde, 0x67, 0xf4, 0x18, 0xcd, 0x4a, 0x6a, 0xed, 0xdd, - 0x5d, 0xad, 0xf6, 0x35, 0xb2, 0xf6, 0xe1, 0x95, 0x77, 0xd7, 0xcb, 0xce, 0x53, 0x9a, 0x95, 0x66, - 0xd4, 0x7b, 0x7a, 0x24, 0xd9, 0xeb, 0xb5, 0xf1, 0x55, 0xf7, 0x99, 0x99, 0xeb, 0xe9, 0xbe, 0xb7, - 0xf7, 0xde, 0xdb, 0x23, 0x8d, 0x7e, 0xa6, 0x00, 0xf3, 0xc3, 0x60, 0x43, 0xaa, 0x5c, 0x14, 0x09, - 0x29, 0x43, 0xf1, 0x07, 0x21, 0x3c, 0xe2, 0x40, 0x70, 0x4c, 0x80, 0x60, 0x5e, 0x49, 0x48, 0x15, - 0xa4, 0x52, 0x84, 0x50, 0x15, 0x9b, 0x0a, 0x95, 0x21, 0x1e, 0x52, 0x45, 0xf1, 0x47, 0x80, 0x22, - 0xf9, 0x23, 0x99, 0x22, 0x21, 0x75, 0x9e, 0xf7, 0x9c, 0xfb, 0xe8, 0xee, 0xd1, 0x4a, 0xe3, 0xf5, - 0xd6, 0xfe, 0xd7, 0x7d, 0xbe, 0xef, 0x7c, 0xe7, 0xdc, 0xf3, 0xfc, 0xce, 0xf7, 0x04, 0x7b, 0xeb, - 0x62, 0x38, 0xe3, 0xfa, 0xe7, 0x9d, 0xa6, 0x7b, 0xbe, 0xea, 0x07, 0xe4, 0xfc, 0xf6, 0x85, 0xf3, - 0x1b, 0xc4, 0x23, 0x81, 0x13, 0x91, 0xda, 0x4c, 0x33, 0xf0, 0x23, 0x1f, 0x21, 0x8e, 0x33, 0xe3, - 0x34, 0xdd, 0x19, 0x8a, 0x33, 0xb3, 0x7d, 0x61, 0xfa, 0x99, 0x0d, 0x37, 0xda, 0x6c, 0xdd, 0x9a, - 0xa9, 0xfa, 0x8d, 0xf3, 0x1b, 0xfe, 0x86, 0x7f, 0x9e, 0xa1, 0xde, 0x6a, 0xad, 0xb3, 0x7f, 0xec, - 0x0f, 0xfb, 0xc5, 0x49, 0x4c, 0x3f, 0x1f, 0x37, 0xd3, 0x70, 0xaa, 0x9b, 0xae, 0x47, 0x82, 0x9d, - 0xf3, 0xcd, 0xad, 0x0d, 0xd6, 0x6e, 0x40, 0x42, 0xbf, 0x15, 0x54, 0x49, 0xb2, 0xe1, 0xb6, 0xb5, - 0xc2, 0xf3, 0x0d, 0x12, 0x39, 0x19, 0xdd, 0x9d, 0x3e, 0x9f, 0x57, 0x2b, 0x68, 0x79, 0x91, 0xdb, - 0x48, 0x37, 0xf3, 0xa1, 0x4e, 0x15, 0xc2, 0xea, 0x26, 0x69, 0x38, 0xa9, 0x7a, 0xcf, 0xe5, 0xd5, - 0x6b, 0x45, 0x6e, 0xfd, 0xbc, 0xeb, 0x45, 0x61, 0x14, 0x24, 0x2b, 0xd9, 0xdf, 0xb0, 0xe0, 0xcc, - 0xec, 0xcd, 0xca, 0x62, 0xdd, 0x09, 0x23, 0xb7, 0x3a, 0x57, 0xf7, 0xab, 0x5b, 0x95, 0xc8, 0x0f, - 0xc8, 0x0d, 0xbf, 0xde, 0x6a, 0x90, 0x0a, 0x1b, 0x08, 0xf4, 0x34, 0x0c, 0x6c, 0xb3, 0xff, 0xcb, - 0x0b, 0x53, 0xd6, 0x19, 0xeb, 0xdc, 0xe0, 0xdc, 0xf8, 0xef, 0xed, 0x96, 0x3e, 0xb0, 0xb7, 0x5b, - 0x1a, 0xb8, 0x21, 0xca, 0xb1, 0xc2, 0x40, 0x67, 0xa1, 0x6f, 0x3d, 0x5c, 0xdb, 0x69, 0x92, 0xa9, - 0x02, 0xc3, 0x1d, 0x15, 0xb8, 0x7d, 0x4b, 0x15, 0x5a, 0x8a, 0x05, 0x14, 0x9d, 0x87, 0xc1, 0xa6, - 0x13, 0x44, 0x6e, 0xe4, 0xfa, 0xde, 0x54, 0xf1, 0x8c, 0x75, 0xae, 0x77, 0x6e, 0x42, 0xa0, 0x0e, - 0x96, 0x25, 0x00, 0xc7, 0x38, 0xb4, 0x1b, 0x01, 0x71, 0x6a, 0xd7, 0xbc, 0xfa, 0xce, 0x54, 0xcf, - 0x19, 0xeb, 0xdc, 0x40, 0xdc, 0x0d, 0x2c, 0xca, 0xb1, 0xc2, 0xb0, 0xbf, 0x54, 0x80, 0x81, 0xd9, - 0xf5, 0x75, 0xd7, 0x73, 0xa3, 0x1d, 0x74, 0x03, 0x86, 0x3d, 0xbf, 0x46, 0xe4, 0x7f, 0xf6, 0x15, - 0x43, 0xcf, 0x9e, 0x99, 0x49, 0x2f, 0xa5, 0x99, 0x55, 0x0d, 0x6f, 0x6e, 0x7c, 0x6f, 0xb7, 0x34, - 0xac, 0x97, 0x60, 0x83, 0x0e, 0xc2, 0x30, 0xd4, 0xf4, 0x6b, 0x8a, 0x6c, 0x81, 0x91, 0x2d, 0x65, - 0x91, 0x2d, 0xc7, 0x68, 0x73, 0x63, 0x7b, 0xbb, 0xa5, 0x21, 0xad, 0x00, 0xeb, 0x44, 0xd0, 0x2d, - 0x18, 0xa3, 0x7f, 0xbd, 0xc8, 0x55, 0x74, 0x8b, 0x8c, 0xee, 0x23, 0x79, 0x74, 0x35, 0xd4, 0xb9, - 0xc9, 0xbd, 0xdd, 0xd2, 0x58, 0xa2, 0x10, 0x27, 0x09, 0xda, 0x3f, 0x6c, 0xc1, 0xd8, 0x6c, 0xb3, - 0x39, 0x1b, 0x34, 0xfc, 0xa0, 0x1c, 0xf8, 0xeb, 0x6e, 0x9d, 0xa0, 0x17, 0xa1, 0x27, 0xa2, 0xb3, - 0xc6, 0x67, 0xf8, 0x11, 0x31, 0xb4, 0x3d, 0x74, 0xae, 0xf6, 0x77, 0x4b, 0x93, 0x09, 0x74, 0x36, - 0x95, 0xac, 0x02, 0x7a, 0x0d, 0xc6, 0xeb, 0x7e, 0xd5, 0xa9, 0x6f, 0xfa, 0x61, 0x24, 0xa0, 0x62, - 0xea, 0x8f, 0xec, 0xed, 0x96, 0xc6, 0xaf, 0x26, 0x60, 0x38, 0x85, 0x6d, 0xdf, 0x85, 0xd1, 0xd9, - 0x28, 0x72, 0xaa, 0x9b, 0xa4, 0xc6, 0x17, 0x14, 0x7a, 0x1e, 0x7a, 0x3c, 0xa7, 0x21, 0x3b, 0x73, - 0x46, 0x76, 0x66, 0xd5, 0x69, 0xd0, 0xce, 0x8c, 0x5f, 0xf7, 0xdc, 0xb7, 0x5b, 0x62, 0x91, 0xd2, - 0x32, 0xcc, 0xb0, 0xd1, 0xb3, 0x00, 0x35, 0xb2, 0xed, 0x56, 0x49, 0xd9, 0x89, 0x36, 0x45, 0x1f, - 0x90, 0xa8, 0x0b, 0x0b, 0x0a, 0x82, 0x35, 0x2c, 0xfb, 0x0e, 0x0c, 0xce, 0x6e, 0xfb, 0x6e, 0xad, - 0xec, 0xd7, 0x42, 0xb4, 0x05, 0x63, 0xcd, 0x80, 0xac, 0x93, 0x40, 0x15, 0x4d, 0x59, 0x67, 0x8a, - 0xe7, 0x86, 0x9e, 0x3d, 0x97, 0x39, 0xf6, 0x26, 0xea, 0xa2, 0x17, 0x05, 0x3b, 0x73, 0xc7, 0x45, - 0x7b, 0x63, 0x09, 0x28, 0x4e, 0x52, 0xb6, 0x7f, 0xb7, 0x00, 0x47, 0x67, 0xef, 0xb6, 0x02, 0xb2, - 0xe0, 0x86, 0x5b, 0xc9, 0x0d, 0x57, 0x73, 0xc3, 0xad, 0xd5, 0x78, 0x04, 0xd4, 0x4a, 0x5f, 0x10, - 0xe5, 0x58, 0x61, 0xa0, 0x67, 0xa0, 0x9f, 0xfe, 0xbe, 0x8e, 0x97, 0xc5, 0x27, 0x4f, 0x0a, 0xe4, - 0xa1, 0x05, 0x27, 0x72, 0x16, 0x38, 0x08, 0x4b, 0x1c, 0xb4, 0x02, 0x43, 0x55, 0x76, 0x3e, 0x6c, - 0xac, 0xf8, 0x35, 0xc2, 0xd6, 0xd6, 0xe0, 0xdc, 0x53, 0x14, 0x7d, 0x3e, 0x2e, 0xde, 0xdf, 0x2d, - 0x4d, 0xf1, 0xbe, 0x09, 0x12, 0x1a, 0x0c, 0xeb, 0xf5, 0x91, 0xad, 0xb6, 0x7b, 0x0f, 0xa3, 0x04, - 0x19, 0x5b, 0xfd, 0x9c, 0xb6, 0x73, 0x7b, 0xd9, 0xce, 0x1d, 0xce, 0xde, 0xb5, 0xe8, 0x02, 0xf4, - 0x6c, 0xb9, 0x5e, 0x6d, 0xaa, 0x8f, 0xd1, 0x3a, 0x45, 0xe7, 0xfc, 0x8a, 0xeb, 0xd5, 0xf6, 0x77, - 0x4b, 0x13, 0x46, 0x77, 0x68, 0x21, 0x66, 0xa8, 0xf6, 0xff, 0xb0, 0xa0, 0xc4, 0x60, 0x4b, 0x6e, - 0x9d, 0x94, 0x49, 0x10, 0xba, 0x61, 0x44, 0xbc, 0xc8, 0x18, 0xd0, 0x67, 0x01, 0x42, 0x52, 0x0d, - 0x48, 0xa4, 0x0d, 0xa9, 0x5a, 0x18, 0x15, 0x05, 0xc1, 0x1a, 0x16, 0x3d, 0x9f, 0xc2, 0x4d, 0x27, - 0x60, 0xeb, 0x4b, 0x0c, 0xac, 0x3a, 0x9f, 0x2a, 0x12, 0x80, 0x63, 0x1c, 0xe3, 0x7c, 0x2a, 0x76, - 0x3a, 0x9f, 0xd0, 0x47, 0x60, 0x2c, 0x6e, 0x2c, 0x6c, 0x3a, 0x55, 0x39, 0x80, 0x6c, 0x07, 0x57, - 0x4c, 0x10, 0x4e, 0xe2, 0xda, 0xff, 0xc4, 0x12, 0x8b, 0x87, 0x7e, 0xf5, 0xbb, 0xfc, 0x5b, 0xed, - 0x5f, 0xb3, 0xa0, 0x7f, 0xce, 0xf5, 0x6a, 0xae, 0xb7, 0x81, 0x3e, 0x05, 0x03, 0xf4, 0xaa, 0xac, - 0x39, 0x91, 0x23, 0x8e, 0xe1, 0x0f, 0x6a, 0x7b, 0x4b, 0xdd, 0x5c, 0x33, 0xcd, 0xad, 0x0d, 0x5a, - 0x10, 0xce, 0x50, 0x6c, 0xba, 0xdb, 0xae, 0xdd, 0xfa, 0x34, 0xa9, 0x46, 0x2b, 0x24, 0x72, 0xe2, - 0xcf, 0x89, 0xcb, 0xb0, 0xa2, 0x8a, 0xae, 0x40, 0x5f, 0xe4, 0x04, 0x1b, 0x24, 0x12, 0xe7, 0x71, - 0xe6, 0xb9, 0xc9, 0x6b, 0x62, 0xba, 0x23, 0x89, 0x57, 0x25, 0xf1, 0x2d, 0xb5, 0xc6, 0xaa, 0x62, - 0x41, 0xc2, 0xfe, 0x3f, 0xfd, 0x70, 0x62, 0xbe, 0xb2, 0x9c, 0xb3, 0xae, 0xce, 0x42, 0x5f, 0x2d, - 0x70, 0xb7, 0x49, 0x20, 0xc6, 0x59, 0x51, 0x59, 0x60, 0xa5, 0x58, 0x40, 0xd1, 0x45, 0x18, 0xe6, - 0xf7, 0xe3, 0x65, 0xc7, 0xab, 0xc5, 0xc7, 0xa3, 0xc0, 0x1e, 0xbe, 0xa1, 0xc1, 0xb0, 0x81, 0x79, - 0xc0, 0x45, 0x75, 0x36, 0xb1, 0x19, 0xf3, 0xee, 0xde, 0xcf, 0x5b, 0x30, 0xce, 0x9b, 0x99, 0x8d, - 0xa2, 0xc0, 0xbd, 0xd5, 0x8a, 0x48, 0x38, 0xd5, 0xcb, 0x4e, 0xba, 0xf9, 0xac, 0xd1, 0xca, 0x1d, - 0x81, 0x99, 0x1b, 0x09, 0x2a, 0xfc, 0x10, 0x9c, 0x12, 0xed, 0x8e, 0x27, 0xc1, 0x38, 0xd5, 0x2c, - 0xfa, 0x3e, 0x0b, 0xa6, 0xab, 0xbe, 0x17, 0x05, 0x7e, 0xbd, 0x4e, 0x82, 0x72, 0xeb, 0x56, 0xdd, - 0x0d, 0x37, 0xf9, 0x3a, 0xc5, 0x64, 0x9d, 0x9d, 0x04, 0x39, 0x73, 0xa8, 0x90, 0xc4, 0x1c, 0x9e, - 0xde, 0xdb, 0x2d, 0x4d, 0xcf, 0xe7, 0x92, 0xc2, 0x6d, 0x9a, 0x41, 0x5b, 0x80, 0xe8, 0xcd, 0x5e, - 0x89, 0x9c, 0x0d, 0x12, 0x37, 0xde, 0xdf, 0x7d, 0xe3, 0xc7, 0xf6, 0x76, 0x4b, 0x68, 0x35, 0x45, - 0x02, 0x67, 0x90, 0x45, 0x6f, 0xc3, 0x11, 0x5a, 0x9a, 0xfa, 0xd6, 0x81, 0xee, 0x9b, 0x9b, 0xda, - 0xdb, 0x2d, 0x1d, 0x59, 0xcd, 0x20, 0x82, 0x33, 0x49, 0xa3, 0xef, 0xb1, 0xe0, 0x44, 0xfc, 0xf9, - 0x8b, 0x77, 0x9a, 0x8e, 0x57, 0x8b, 0x1b, 0x1e, 0xec, 0xbe, 0x61, 0x7a, 0x26, 0x9f, 0x98, 0xcf, - 0xa3, 0x84, 0xf3, 0x1b, 0x41, 0x1e, 0x4c, 0xd2, 0xae, 0x25, 0xdb, 0x86, 0xee, 0xdb, 0x3e, 0xbe, - 0xb7, 0x5b, 0x9a, 0x5c, 0x4d, 0xd3, 0xc0, 0x59, 0x84, 0xa7, 0xe7, 0xe1, 0x68, 0xe6, 0xea, 0x44, - 0xe3, 0x50, 0xdc, 0x22, 0x9c, 0x09, 0x1c, 0xc4, 0xf4, 0x27, 0x3a, 0x02, 0xbd, 0xdb, 0x4e, 0xbd, - 0x25, 0x36, 0x26, 0xe6, 0x7f, 0x5e, 0x2a, 0x5c, 0xb4, 0xec, 0x7f, 0x5b, 0x84, 0xb1, 0xf9, 0xca, - 0xf2, 0x3d, 0xed, 0x7a, 0xfd, 0xda, 0x2b, 0xb4, 0xbd, 0xf6, 0xe2, 0x4b, 0xb4, 0x98, 0x7b, 0x89, - 0x7e, 0x77, 0xc6, 0x96, 0xed, 0x61, 0x5b, 0xf6, 0xc3, 0x39, 0x5b, 0xf6, 0x3e, 0x6f, 0xd4, 0xed, - 0x9c, 0x55, 0xdb, 0xcb, 0x26, 0x30, 0x93, 0x43, 0x62, 0xbc, 0x5f, 0xf2, 0xa8, 0x3d, 0xe0, 0xd2, - 0xbd, 0x3f, 0xf3, 0x58, 0x85, 0xe1, 0x79, 0xa7, 0xe9, 0xdc, 0x72, 0xeb, 0x6e, 0xe4, 0x92, 0x10, - 0x3d, 0x0e, 0x45, 0xa7, 0x56, 0x63, 0xdc, 0xdd, 0xe0, 0xdc, 0xd1, 0xbd, 0xdd, 0x52, 0x71, 0xb6, - 0x46, 0xd9, 0x0c, 0x50, 0x58, 0x3b, 0x98, 0x62, 0xa0, 0x27, 0xa1, 0xa7, 0x16, 0xf8, 0xcd, 0xa9, - 0x02, 0xc3, 0xa4, 0xbb, 0xbc, 0x67, 0x21, 0xf0, 0x9b, 0x09, 0x54, 0x86, 0x63, 0xff, 0x4e, 0x01, - 0x4e, 0xce, 0x93, 0xe6, 0xe6, 0x52, 0x25, 0xe7, 0xbe, 0x38, 0x07, 0x03, 0x0d, 0xdf, 0x73, 0x23, - 0x3f, 0x08, 0x45, 0xd3, 0x6c, 0x45, 0xac, 0x88, 0x32, 0xac, 0xa0, 0xe8, 0x0c, 0xf4, 0x34, 0x63, - 0x26, 0x76, 0x58, 0x32, 0xc0, 0x8c, 0x7d, 0x65, 0x10, 0x8a, 0xd1, 0x0a, 0x49, 0x20, 0x56, 0x8c, - 0xc2, 0xb8, 0x1e, 0x92, 0x00, 0x33, 0x48, 0xcc, 0x09, 0x50, 0x1e, 0x41, 0xdc, 0x08, 0x09, 0x4e, - 0x80, 0x42, 0xb0, 0x86, 0x85, 0xca, 0x30, 0x18, 0x26, 0x66, 0xb6, 0xab, 0xad, 0x39, 0xc2, 0x58, - 0x05, 0x35, 0x93, 0x31, 0x11, 0xe3, 0x06, 0xeb, 0xeb, 0xc8, 0x2a, 0x7c, 0xad, 0x00, 0x88, 0x0f, - 0xe1, 0xb7, 0xd9, 0xc0, 0x5d, 0x4f, 0x0f, 0x5c, 0xf7, 0x5b, 0xe2, 0x7e, 0x8d, 0xde, 0xff, 0xb4, - 0xe0, 0xe4, 0xbc, 0xeb, 0xd5, 0x48, 0x90, 0xb3, 0x00, 0x1f, 0xcc, 0x53, 0xfe, 0x60, 0x4c, 0x8a, - 0xb1, 0xc4, 0x7a, 0xee, 0xc3, 0x12, 0xb3, 0xff, 0xca, 0x02, 0xc4, 0x3f, 0xfb, 0x5d, 0xf7, 0xb1, - 0xd7, 0xd3, 0x1f, 0x7b, 0x1f, 0x96, 0x85, 0x7d, 0x15, 0x46, 0xe7, 0xeb, 0x2e, 0xf1, 0xa2, 0xe5, - 0xf2, 0xbc, 0xef, 0xad, 0xbb, 0x1b, 0xe8, 0x25, 0x18, 0x8d, 0xdc, 0x06, 0xf1, 0x5b, 0x51, 0x85, - 0x54, 0x7d, 0x8f, 0xbd, 0x5c, 0xad, 0x73, 0xbd, 0x73, 0x68, 0x6f, 0xb7, 0x34, 0xba, 0x66, 0x40, - 0x70, 0x02, 0xd3, 0xfe, 0x19, 0x7a, 0x6e, 0xd5, 0x5b, 0x61, 0x44, 0x82, 0xb5, 0xa0, 0x15, 0x46, - 0x73, 0x2d, 0xca, 0x7b, 0x96, 0x03, 0x9f, 0x76, 0xc7, 0xf5, 0x3d, 0x74, 0xd2, 0x78, 0x8e, 0x0f, - 0xc8, 0xa7, 0xb8, 0x78, 0x76, 0xcf, 0x00, 0x84, 0xee, 0x86, 0x47, 0x02, 0xed, 0xf9, 0x30, 0xca, - 0xb6, 0x8a, 0x2a, 0xc5, 0x1a, 0x06, 0xaa, 0xc3, 0x48, 0xdd, 0xb9, 0x45, 0xea, 0x15, 0x52, 0x27, - 0xd5, 0xc8, 0x0f, 0x84, 0x7c, 0xe3, 0xb9, 0xee, 0xde, 0x01, 0x57, 0xf5, 0xaa, 0x73, 0x13, 0x7b, - 0xbb, 0xa5, 0x11, 0xa3, 0x08, 0x9b, 0xc4, 0xe9, 0xd1, 0xe1, 0x37, 0xe9, 0x57, 0x38, 0x75, 0xfd, - 0xf1, 0x79, 0x4d, 0x94, 0x61, 0x05, 0x55, 0x47, 0x47, 0x4f, 0xde, 0xd1, 0x61, 0xff, 0x09, 0x5d, - 0x68, 0x7e, 0xa3, 0xe9, 0x7b, 0xc4, 0x8b, 0xe6, 0x7d, 0xaf, 0xc6, 0x25, 0x53, 0x2f, 0x19, 0xa2, - 0x93, 0xb3, 0x09, 0xd1, 0xc9, 0xb1, 0x74, 0x0d, 0x4d, 0x7a, 0xf2, 0x61, 0xe8, 0x0b, 0x23, 0x27, - 0x6a, 0x85, 0x62, 0xe0, 0x1e, 0x96, 0xcb, 0xae, 0xc2, 0x4a, 0xf7, 0x77, 0x4b, 0x63, 0xaa, 0x1a, - 0x2f, 0xc2, 0xa2, 0x02, 0x7a, 0x02, 0xfa, 0x1b, 0x24, 0x0c, 0x9d, 0x0d, 0xc9, 0x36, 0x8c, 0x89, - 0xba, 0xfd, 0x2b, 0xbc, 0x18, 0x4b, 0x38, 0x7a, 0x04, 0x7a, 0x49, 0x10, 0xf8, 0x81, 0xf8, 0xb6, - 0x11, 0x81, 0xd8, 0xbb, 0x48, 0x0b, 0x31, 0x87, 0xd9, 0xff, 0xc1, 0x82, 0x31, 0xd5, 0x57, 0xde, - 0xd6, 0x21, 0x3c, 0xd7, 0xde, 0x04, 0xa8, 0xca, 0x0f, 0x0c, 0xd9, 0x35, 0x3b, 0xf4, 0xec, 0xd9, - 0x4c, 0x8e, 0x26, 0x35, 0x8c, 0x31, 0x65, 0x55, 0x14, 0x62, 0x8d, 0x9a, 0xfd, 0x9b, 0x16, 0x4c, - 0x26, 0xbe, 0xe8, 0xaa, 0x1b, 0x46, 0xe8, 0xad, 0xd4, 0x57, 0xcd, 0x74, 0xb9, 0xf8, 0xdc, 0x90, - 0x7f, 0x93, 0xda, 0xf3, 0xb2, 0x44, 0xfb, 0xa2, 0xcb, 0xd0, 0xeb, 0x46, 0xa4, 0x21, 0x3f, 0xe6, - 0x91, 0xb6, 0x1f, 0xc3, 0x7b, 0x15, 0xcf, 0xc8, 0x32, 0xad, 0x89, 0x39, 0x01, 0xfb, 0x77, 0x8a, - 0x30, 0xc8, 0xf7, 0xf7, 0x8a, 0xd3, 0x3c, 0x84, 0xb9, 0x78, 0x0a, 0x06, 0xdd, 0x46, 0xa3, 0x15, - 0x39, 0xb7, 0xc4, 0xbd, 0x37, 0xc0, 0xcf, 0xa0, 0x65, 0x59, 0x88, 0x63, 0x38, 0x5a, 0x86, 0x1e, - 0xd6, 0x15, 0xfe, 0x95, 0x8f, 0x67, 0x7f, 0xa5, 0xe8, 0xfb, 0xcc, 0x82, 0x13, 0x39, 0x9c, 0xe5, - 0x54, 0xfb, 0x8a, 0x16, 0x61, 0x46, 0x02, 0x39, 0x00, 0xb7, 0x5c, 0xcf, 0x09, 0x76, 0x68, 0xd9, - 0x54, 0x91, 0x11, 0x7c, 0xa6, 0x3d, 0xc1, 0x39, 0x85, 0xcf, 0xc9, 0xaa, 0x0f, 0x8b, 0x01, 0x58, - 0x23, 0x3a, 0xfd, 0x22, 0x0c, 0x2a, 0xe4, 0x83, 0x70, 0x8e, 0xd3, 0x1f, 0x81, 0xb1, 0x44, 0x5b, - 0x9d, 0xaa, 0x0f, 0xeb, 0x8c, 0xe7, 0xaf, 0xb3, 0x23, 0x43, 0xf4, 0x7a, 0xd1, 0xdb, 0x16, 0x77, - 0xd3, 0x5d, 0x38, 0x52, 0xcf, 0x38, 0xf2, 0xc5, 0xbc, 0x76, 0x7f, 0x45, 0x9c, 0x14, 0x9f, 0x7d, - 0x24, 0x0b, 0x8a, 0x33, 0xdb, 0x30, 0x4e, 0xc4, 0x42, 0xbb, 0x13, 0x91, 0x9e, 0x77, 0x47, 0x54, - 0xe7, 0xaf, 0x90, 0x1d, 0x75, 0xa8, 0x7e, 0x2b, 0xbb, 0x7f, 0x8a, 0x8f, 0x3e, 0x3f, 0x2e, 0x87, - 0x04, 0x81, 0xe2, 0x15, 0xb2, 0xc3, 0xa7, 0x42, 0xff, 0xba, 0x62, 0xdb, 0xaf, 0xfb, 0x8a, 0x05, - 0x23, 0xea, 0xeb, 0x0e, 0xe1, 0x5c, 0x98, 0x33, 0xcf, 0x85, 0x53, 0x6d, 0x17, 0x78, 0xce, 0x89, - 0xf0, 0xb5, 0x02, 0x9c, 0x50, 0x38, 0xf4, 0x11, 0xc5, 0xff, 0x88, 0x55, 0x75, 0x1e, 0x06, 0x3d, - 0x25, 0x4e, 0xb4, 0x4c, 0x39, 0x5e, 0x2c, 0x4c, 0x8c, 0x71, 0xe8, 0x95, 0xe7, 0xc5, 0x97, 0xf6, - 0xb0, 0x2e, 0x67, 0x17, 0x97, 0xfb, 0x1c, 0x14, 0x5b, 0x6e, 0x4d, 0x5c, 0x30, 0x1f, 0x94, 0xa3, - 0x7d, 0x7d, 0x79, 0x61, 0x7f, 0xb7, 0xf4, 0x70, 0x9e, 0xca, 0x89, 0xde, 0x6c, 0xe1, 0xcc, 0xf5, - 0xe5, 0x05, 0x4c, 0x2b, 0xa3, 0x59, 0x18, 0x93, 0x5a, 0xb5, 0x1b, 0x94, 0x2f, 0xf5, 0x3d, 0x71, - 0x0f, 0x29, 0x61, 0x39, 0x36, 0xc1, 0x38, 0x89, 0x8f, 0x16, 0x60, 0x7c, 0xab, 0x75, 0x8b, 0xd4, - 0x49, 0xc4, 0x3f, 0xf8, 0x0a, 0xe1, 0xa2, 0xe4, 0xc1, 0xf8, 0x09, 0x7b, 0x25, 0x01, 0xc7, 0xa9, - 0x1a, 0xf6, 0xdf, 0xb1, 0xfb, 0x40, 0x8c, 0x9e, 0xc6, 0xdf, 0x7c, 0x2b, 0x97, 0x73, 0x37, 0xab, - 0xe2, 0x0a, 0xd9, 0x59, 0xf3, 0x29, 0x1f, 0x92, 0xbd, 0x2a, 0x8c, 0x35, 0xdf, 0xd3, 0x76, 0xcd, - 0xff, 0x72, 0x01, 0x8e, 0xaa, 0x11, 0x30, 0xb8, 0xe5, 0x6f, 0xf7, 0x31, 0xb8, 0x00, 0x43, 0x35, - 0xb2, 0xee, 0xb4, 0xea, 0x91, 0xd2, 0x6b, 0xf4, 0x72, 0x55, 0xdb, 0x42, 0x5c, 0x8c, 0x75, 0x9c, - 0x03, 0x0c, 0xdb, 0x2f, 0x8c, 0xb0, 0x8b, 0x38, 0x72, 0xe8, 0x1a, 0x57, 0xbb, 0xc6, 0xca, 0xdd, - 0x35, 0x8f, 0x40, 0xaf, 0xdb, 0xa0, 0x8c, 0x59, 0xc1, 0xe4, 0xb7, 0x96, 0x69, 0x21, 0xe6, 0x30, - 0xf4, 0x18, 0xf4, 0x57, 0xfd, 0x46, 0xc3, 0xf1, 0x6a, 0xec, 0xca, 0x1b, 0x9c, 0x1b, 0xa2, 0xbc, - 0xdb, 0x3c, 0x2f, 0xc2, 0x12, 0x46, 0x99, 0x6f, 0x27, 0xd8, 0xe0, 0xc2, 0x1e, 0xc1, 0x7c, 0xcf, - 0x06, 0x1b, 0x21, 0x66, 0xa5, 0xf4, 0xad, 0x7a, 0xdb, 0x0f, 0xb6, 0x5c, 0x6f, 0x63, 0xc1, 0x0d, - 0xc4, 0x96, 0x50, 0x77, 0xe1, 0x4d, 0x05, 0xc1, 0x1a, 0x16, 0x5a, 0x82, 0xde, 0xa6, 0x1f, 0x44, - 0xe1, 0x54, 0x1f, 0x1b, 0xee, 0x87, 0x73, 0x0e, 0x22, 0xfe, 0xb5, 0x65, 0x3f, 0x88, 0xe2, 0x0f, - 0xa0, 0xff, 0x42, 0xcc, 0xab, 0xa3, 0xab, 0xd0, 0x4f, 0xbc, 0xed, 0xa5, 0xc0, 0x6f, 0x4c, 0x4d, - 0xe6, 0x53, 0x5a, 0xe4, 0x28, 0x7c, 0x99, 0xc5, 0x3c, 0xaa, 0x28, 0xc6, 0x92, 0x04, 0xfa, 0x30, - 0x14, 0x89, 0xb7, 0x3d, 0xd5, 0xcf, 0x28, 0x4d, 0xe7, 0x50, 0xba, 0xe1, 0x04, 0xf1, 0x99, 0xbf, - 0xe8, 0x6d, 0x63, 0x5a, 0x07, 0x7d, 0x0c, 0x06, 0xe5, 0x81, 0x11, 0x0a, 0x29, 0x6a, 0xe6, 0x82, - 0x95, 0xc7, 0x0c, 0x26, 0x6f, 0xb7, 0xdc, 0x80, 0x34, 0x88, 0x17, 0x85, 0xf1, 0x09, 0x29, 0xa1, - 0x21, 0x8e, 0xa9, 0xa1, 0x2a, 0x0c, 0x07, 0x24, 0x74, 0xef, 0x92, 0xb2, 0x5f, 0x77, 0xab, 0x3b, - 0x53, 0xc7, 0x59, 0xf7, 0x9e, 0x68, 0x3b, 0x64, 0x58, 0xab, 0x10, 0x4b, 0xf9, 0xf5, 0x52, 0x6c, - 0x10, 0x45, 0x6f, 0xc0, 0x48, 0x40, 0xc2, 0xc8, 0x09, 0x22, 0xd1, 0xca, 0x94, 0xd2, 0xca, 0x8d, - 0x60, 0x1d, 0xc0, 0x9f, 0x13, 0x71, 0x33, 0x31, 0x04, 0x9b, 0x14, 0xd0, 0xc7, 0xa4, 0xca, 0x61, - 0xc5, 0x6f, 0x79, 0x51, 0x38, 0x35, 0xc8, 0xfa, 0x9d, 0xa9, 0x9b, 0xbe, 0x11, 0xe3, 0x25, 0x75, - 0x12, 0xbc, 0x32, 0x36, 0x48, 0xa1, 0x4f, 0xc0, 0x08, 0xff, 0xcf, 0x55, 0xaa, 0xe1, 0xd4, 0x51, - 0x46, 0xfb, 0x4c, 0x3e, 0x6d, 0x8e, 0x38, 0x77, 0x54, 0x10, 0x1f, 0xd1, 0x4b, 0x43, 0x6c, 0x52, - 0x43, 0x18, 0x46, 0xea, 0xee, 0x36, 0xf1, 0x48, 0x18, 0x96, 0x03, 0xff, 0x16, 0x11, 0x12, 0xe2, - 0x13, 0xd9, 0x2a, 0x58, 0xff, 0x16, 0x11, 0x8f, 0x40, 0xbd, 0x0e, 0x36, 0x49, 0xa0, 0xeb, 0x30, - 0x4a, 0x9f, 0xe4, 0x6e, 0x4c, 0x74, 0xa8, 0x13, 0x51, 0xf6, 0x70, 0xc6, 0x46, 0x25, 0x9c, 0x20, - 0x82, 0xae, 0xc1, 0x30, 0x1b, 0xf3, 0x56, 0x93, 0x13, 0x3d, 0xd6, 0x89, 0x28, 0x33, 0x28, 0xa8, - 0x68, 0x55, 0xb0, 0x41, 0x00, 0xbd, 0x0e, 0x83, 0x75, 0x77, 0x9d, 0x54, 0x77, 0xaa, 0x75, 0x32, - 0x35, 0xcc, 0xa8, 0x65, 0x1e, 0x86, 0x57, 0x25, 0x12, 0xe7, 0xcf, 0xd5, 0x5f, 0x1c, 0x57, 0x47, - 0x37, 0xe0, 0x58, 0x44, 0x82, 0x86, 0xeb, 0x39, 0xf4, 0x10, 0x13, 0x4f, 0x42, 0xa6, 0x19, 0x1f, - 0x61, 0xab, 0xeb, 0xb4, 0x98, 0x8d, 0x63, 0x6b, 0x99, 0x58, 0x38, 0xa7, 0x36, 0xba, 0x03, 0x53, - 0x19, 0x10, 0xbe, 0x6e, 0x8f, 0x30, 0xca, 0xaf, 0x08, 0xca, 0x53, 0x6b, 0x39, 0x78, 0xfb, 0x6d, - 0x60, 0x38, 0x97, 0x3a, 0xba, 0x06, 0x63, 0xec, 0xe4, 0x2c, 0xb7, 0xea, 0x75, 0xd1, 0xe0, 0x28, - 0x6b, 0xf0, 0x31, 0xc9, 0x47, 0x2c, 0x9b, 0xe0, 0xfd, 0xdd, 0x12, 0xc4, 0xff, 0x70, 0xb2, 0x36, - 0xba, 0xc5, 0x94, 0xb0, 0xad, 0xc0, 0x8d, 0x76, 0xe8, 0xae, 0x22, 0x77, 0xa2, 0xa9, 0xb1, 0xb6, - 0x02, 0x29, 0x1d, 0x55, 0x69, 0x6a, 0xf5, 0x42, 0x9c, 0x24, 0x48, 0xaf, 0x82, 0x30, 0xaa, 0xb9, - 0xde, 0xd4, 0x38, 0x7f, 0x4f, 0xc9, 0x93, 0xb4, 0x42, 0x0b, 0x31, 0x87, 0x31, 0x05, 0x2c, 0xfd, - 0x71, 0x8d, 0xde, 0xb8, 0x13, 0x0c, 0x31, 0x56, 0xc0, 0x4a, 0x00, 0x8e, 0x71, 0x28, 0x13, 0x1c, - 0x45, 0x3b, 0x53, 0x88, 0xa1, 0xaa, 0x03, 0x71, 0x6d, 0xed, 0x63, 0x98, 0x96, 0xdb, 0xb7, 0x60, - 0x54, 0x1d, 0x13, 0x6c, 0x4c, 0x50, 0x09, 0x7a, 0x19, 0xdb, 0x27, 0xc4, 0xa7, 0x83, 0xb4, 0x0b, - 0x8c, 0x25, 0xc4, 0xbc, 0x9c, 0x75, 0xc1, 0xbd, 0x4b, 0xe6, 0x76, 0x22, 0xc2, 0x65, 0x11, 0x45, - 0xad, 0x0b, 0x12, 0x80, 0x63, 0x1c, 0xfb, 0xff, 0x72, 0xf6, 0x39, 0xbe, 0x25, 0xba, 0xb8, 0x17, - 0x9f, 0x86, 0x01, 0x66, 0xf8, 0xe1, 0x07, 0x5c, 0x3b, 0xdb, 0x1b, 0x33, 0xcc, 0x97, 0x45, 0x39, - 0x56, 0x18, 0xe8, 0x65, 0x18, 0xa9, 0xea, 0x0d, 0x88, 0x4b, 0x5d, 0x1d, 0x23, 0x46, 0xeb, 0xd8, - 0xc4, 0x45, 0x17, 0x61, 0x80, 0xd9, 0x38, 0x55, 0xfd, 0xba, 0xe0, 0x36, 0x25, 0x67, 0x32, 0x50, - 0x16, 0xe5, 0xfb, 0xda, 0x6f, 0xac, 0xb0, 0xd1, 0x59, 0xe8, 0xa3, 0x5d, 0x58, 0x2e, 0x8b, 0xeb, - 0x54, 0x49, 0x02, 0x2f, 0xb3, 0x52, 0x2c, 0xa0, 0xf6, 0x6f, 0x5a, 0x8c, 0x97, 0x4a, 0x9f, 0xf9, - 0xe8, 0x32, 0xbb, 0x34, 0xd8, 0x0d, 0xa2, 0x69, 0xe1, 0x1f, 0xd5, 0x6e, 0x02, 0x05, 0xdb, 0x4f, - 0xfc, 0xc7, 0x46, 0x4d, 0xf4, 0x66, 0xf2, 0x66, 0xe0, 0x0c, 0xc5, 0xf3, 0x72, 0x08, 0x92, 0xb7, - 0xc3, 0x43, 0xf1, 0x15, 0x47, 0xfb, 0xd3, 0xee, 0x8a, 0xb0, 0x7f, 0xa4, 0xa0, 0xad, 0x92, 0x4a, - 0xe4, 0x44, 0x04, 0x95, 0xa1, 0xff, 0xb6, 0xe3, 0x46, 0xae, 0xb7, 0x21, 0xf8, 0xbe, 0xf6, 0x17, - 0x1d, 0xab, 0x74, 0x93, 0x57, 0xe0, 0xdc, 0x8b, 0xf8, 0x83, 0x25, 0x19, 0x4a, 0x31, 0x68, 0x79, - 0x1e, 0xa5, 0x58, 0xe8, 0x96, 0x22, 0xe6, 0x15, 0x38, 0x45, 0xf1, 0x07, 0x4b, 0x32, 0xe8, 0x2d, - 0x00, 0x79, 0x42, 0x90, 0x9a, 0x90, 0x1d, 0x3e, 0xdd, 0x99, 0xe8, 0x9a, 0xaa, 0xc3, 0x85, 0x93, - 0xf1, 0x7f, 0xac, 0xd1, 0xb3, 0x23, 0x6d, 0x4e, 0xf5, 0xce, 0xa0, 0x8f, 0xd3, 0x2d, 0xea, 0x04, - 0x11, 0xa9, 0xcd, 0x46, 0x62, 0x70, 0x9e, 0xec, 0xee, 0x71, 0xb8, 0xe6, 0x36, 0x88, 0xbe, 0x9d, - 0x05, 0x11, 0x1c, 0xd3, 0xb3, 0x7f, 0xb5, 0x08, 0x53, 0x79, 0xdd, 0xa5, 0x9b, 0x86, 0xdc, 0x71, - 0xa3, 0x79, 0xca, 0xd6, 0x5a, 0xe6, 0xa6, 0x59, 0x14, 0xe5, 0x58, 0x61, 0xd0, 0xd5, 0x1b, 0xba, - 0x1b, 0xf2, 0x6d, 0xdf, 0x1b, 0xaf, 0xde, 0x0a, 0x2b, 0xc5, 0x02, 0x4a, 0xf1, 0x02, 0xe2, 0x84, - 0xc2, 0xf8, 0x4e, 0x5b, 0xe5, 0x98, 0x95, 0x62, 0x01, 0xd5, 0xa5, 0x8c, 0x3d, 0x1d, 0xa4, 0x8c, - 0xc6, 0x10, 0xf5, 0xde, 0xdf, 0x21, 0x42, 0x9f, 0x04, 0x58, 0x77, 0x3d, 0x37, 0xdc, 0x64, 0xd4, - 0xfb, 0x0e, 0x4c, 0x5d, 0x31, 0xc5, 0x4b, 0x8a, 0x0a, 0xd6, 0x28, 0xa2, 0x17, 0x60, 0x48, 0x1d, - 0x20, 0xcb, 0x0b, 0x4c, 0xf5, 0xaf, 0x99, 0x52, 0xc5, 0xa7, 0xe9, 0x02, 0xd6, 0xf1, 0xec, 0x4f, - 0x27, 0xd7, 0x8b, 0xd8, 0x01, 0xda, 0xf8, 0x5a, 0xdd, 0x8e, 0x6f, 0xa1, 0xfd, 0xf8, 0xda, 0x3f, - 0x36, 0x08, 0x63, 0x46, 0x63, 0xad, 0xb0, 0x8b, 0x33, 0xf7, 0x12, 0xbd, 0x80, 0x9c, 0x88, 0x88, - 0xfd, 0x67, 0x77, 0xde, 0x2a, 0xfa, 0x25, 0x45, 0x77, 0x00, 0xaf, 0x8f, 0x3e, 0x09, 0x83, 0x75, - 0x27, 0x64, 0x12, 0x4b, 0x22, 0xf6, 0x5d, 0x37, 0xc4, 0xe2, 0x07, 0xa1, 0x13, 0x46, 0xda, 0xad, - 0xcf, 0x69, 0xc7, 0x24, 0xe9, 0x4d, 0x49, 0xf9, 0x2b, 0x69, 0xdd, 0xa9, 0x3a, 0x41, 0x99, 0xb0, - 0x1d, 0xcc, 0x61, 0xe8, 0x22, 0x3b, 0x5a, 0xe9, 0xaa, 0x98, 0xa7, 0xdc, 0x28, 0x5b, 0x66, 0xbd, - 0x06, 0x93, 0xad, 0x60, 0xd8, 0xc0, 0x8c, 0xdf, 0x64, 0x7d, 0x6d, 0xde, 0x64, 0x4f, 0x40, 0x3f, - 0xfb, 0xa1, 0x56, 0x80, 0x9a, 0x8d, 0x65, 0x5e, 0x8c, 0x25, 0x3c, 0xb9, 0x60, 0x06, 0xba, 0x5b, - 0x30, 0xf4, 0xd5, 0x27, 0x16, 0x35, 0x33, 0xbb, 0x18, 0xe0, 0xa7, 0x9c, 0x58, 0xf2, 0x58, 0xc2, - 0xd0, 0xcf, 0x5a, 0x80, 0x9c, 0x3a, 0x7d, 0x2d, 0xd3, 0x62, 0xf5, 0xb8, 0x01, 0xc6, 0x6a, 0xbf, - 0xdc, 0x71, 0xd8, 0x5b, 0xe1, 0xcc, 0x6c, 0xaa, 0x36, 0x97, 0x94, 0xbe, 0x24, 0xba, 0x88, 0xd2, - 0x08, 0xfa, 0x65, 0x74, 0xd5, 0x0d, 0xa3, 0xcf, 0xfe, 0x69, 0xe2, 0x72, 0xca, 0xe8, 0x12, 0xba, - 0xae, 0x3f, 0xbe, 0x86, 0x0e, 0xf8, 0xf8, 0x1a, 0xc9, 0x7d, 0x78, 0x7d, 0x67, 0xe2, 0x01, 0x33, - 0xcc, 0xbe, 0xfc, 0xb1, 0x0e, 0x0f, 0x18, 0x21, 0x4e, 0xef, 0xe6, 0x19, 0x53, 0x16, 0x7a, 0xe0, - 0x11, 0xd6, 0xe5, 0xf6, 0x8f, 0xe0, 0xeb, 0x21, 0x09, 0xe6, 0x4e, 0x48, 0x35, 0xf1, 0xbe, 0xce, - 0x7b, 0x68, 0x7a, 0xe3, 0xef, 0xb1, 0x60, 0x2a, 0x3d, 0x40, 0xbc, 0x4b, 0x53, 0xa3, 0xac, 0xff, - 0x76, 0xbb, 0x91, 0x11, 0x9d, 0x97, 0xe6, 0xae, 0x53, 0xb3, 0x39, 0xb4, 0x70, 0x6e, 0x2b, 0xd3, - 0x2d, 0x38, 0x9e, 0x33, 0xef, 0x19, 0x52, 0xeb, 0x05, 0x5d, 0x6a, 0xdd, 0x41, 0xd6, 0x39, 0x23, - 0x67, 0x66, 0xe6, 0x8d, 0x96, 0xe3, 0x45, 0x6e, 0xb4, 0xa3, 0x4b, 0xb9, 0x3d, 0x30, 0x07, 0x04, - 0x7d, 0x02, 0x7a, 0xeb, 0xae, 0xd7, 0xba, 0x23, 0x6e, 0xca, 0xb3, 0xd9, 0x8f, 0x18, 0xaf, 0x75, - 0xc7, 0x1c, 0xe2, 0x12, 0xdd, 0x90, 0xac, 0x7c, 0x7f, 0xb7, 0x84, 0xd2, 0x08, 0x98, 0x53, 0xb5, - 0x9f, 0x84, 0xd1, 0x05, 0x87, 0x34, 0x7c, 0x6f, 0xd1, 0xab, 0x35, 0x7d, 0xd7, 0x8b, 0xd0, 0x14, - 0xf4, 0x30, 0x16, 0x91, 0x5f, 0x90, 0x3d, 0x74, 0x08, 0x31, 0x2b, 0xb1, 0x37, 0xe0, 0xe8, 0x82, - 0x7f, 0xdb, 0xbb, 0xed, 0x04, 0xb5, 0xd9, 0xf2, 0xb2, 0x26, 0xf5, 0x5b, 0x95, 0x52, 0x27, 0x2b, - 0xff, 0x4d, 0xaf, 0xd5, 0xe4, 0x4b, 0x69, 0xc9, 0xad, 0x93, 0x1c, 0xd9, 0xec, 0x8f, 0x15, 0x8c, - 0x96, 0x62, 0x7c, 0xa5, 0x59, 0xb4, 0x72, 0x8d, 0x12, 0xde, 0x80, 0x81, 0x75, 0x97, 0xd4, 0x6b, - 0x98, 0xac, 0x8b, 0xd9, 0x78, 0x3c, 0xdf, 0x6c, 0x71, 0x89, 0x62, 0x2a, 0x15, 0x28, 0x93, 0x59, - 0x2d, 0x89, 0xca, 0x58, 0x91, 0x41, 0x5b, 0x30, 0x2e, 0xe7, 0x4c, 0x42, 0xc5, 0xa9, 0xfd, 0x44, - 0xbb, 0x45, 0x68, 0x12, 0x67, 0x26, 0xdc, 0x38, 0x41, 0x06, 0xa7, 0x08, 0xa3, 0x93, 0xd0, 0xd3, - 0xa0, 0xfc, 0x49, 0x0f, 0x1b, 0x7e, 0x26, 0xa4, 0x62, 0xf2, 0x36, 0x56, 0x6a, 0xff, 0x84, 0x05, - 0xc7, 0x53, 0x23, 0x23, 0xe4, 0x8e, 0xf7, 0x79, 0x16, 0x92, 0x72, 0xc0, 0x42, 0x67, 0x39, 0xa0, - 0xfd, 0x4f, 0x2d, 0x38, 0xb2, 0xd8, 0x68, 0x46, 0x3b, 0x0b, 0xae, 0x69, 0x41, 0xf0, 0x22, 0xf4, - 0x35, 0x48, 0xcd, 0x6d, 0x35, 0xc4, 0xcc, 0x95, 0xe4, 0x1d, 0xbe, 0xc2, 0x4a, 0xe9, 0x39, 0x50, - 0x89, 0xfc, 0xc0, 0xd9, 0x20, 0xbc, 0x00, 0x0b, 0x74, 0xc6, 0x09, 0xb9, 0x77, 0xc9, 0x55, 0xb7, - 0xe1, 0x46, 0xf7, 0xb6, 0xbb, 0x84, 0xf2, 0x5f, 0x12, 0xc1, 0x31, 0x3d, 0xfb, 0x1b, 0x16, 0x8c, - 0xc9, 0x75, 0x3f, 0x5b, 0xab, 0x05, 0x24, 0x0c, 0xd1, 0x34, 0x14, 0xdc, 0xa6, 0xe8, 0x25, 0x88, - 0x5e, 0x16, 0x96, 0xcb, 0xb8, 0xe0, 0x36, 0xe5, 0xa3, 0x8b, 0xb1, 0x09, 0x45, 0xd3, 0x0e, 0xe2, - 0xb2, 0x28, 0xc7, 0x0a, 0x03, 0x9d, 0x83, 0x01, 0xcf, 0xaf, 0xf1, 0x77, 0x8b, 0xd0, 0x84, 0x53, - 0xcc, 0x55, 0x51, 0x86, 0x15, 0x14, 0x95, 0x61, 0x90, 0x5b, 0xc9, 0xc6, 0x8b, 0xb6, 0x2b, 0x5b, - 0x5b, 0xf6, 0x65, 0x6b, 0xb2, 0x26, 0x8e, 0x89, 0xd8, 0xbf, 0x6d, 0xc1, 0xb0, 0xfc, 0xb2, 0x2e, - 0x5f, 0x94, 0x74, 0x6b, 0xc5, 0xaf, 0xc9, 0x78, 0x6b, 0xd1, 0x17, 0x21, 0x83, 0x18, 0x0f, 0xc1, - 0xe2, 0x81, 0x1e, 0x82, 0x17, 0x60, 0xc8, 0x69, 0x36, 0xcb, 0xe6, 0x2b, 0x92, 0x2d, 0xa5, 0xd9, - 0xb8, 0x18, 0xeb, 0x38, 0xf6, 0x8f, 0x17, 0x60, 0x54, 0x7e, 0x41, 0xa5, 0x75, 0x2b, 0x24, 0x11, - 0x5a, 0x83, 0x41, 0x87, 0xcf, 0x12, 0x91, 0x8b, 0xfc, 0x91, 0x6c, 0xe9, 0xa6, 0x31, 0xa5, 0x31, - 0x3b, 0x3c, 0x2b, 0x6b, 0xe3, 0x98, 0x10, 0xaa, 0xc3, 0x84, 0xe7, 0x47, 0x8c, 0x35, 0x52, 0xf0, - 0x76, 0x0a, 0xe7, 0x24, 0xf5, 0x13, 0x82, 0xfa, 0xc4, 0x6a, 0x92, 0x0a, 0x4e, 0x13, 0x46, 0x8b, - 0x52, 0x62, 0x5c, 0xcc, 0x17, 0xf5, 0xe9, 0x13, 0x97, 0x2d, 0x30, 0xb6, 0x7f, 0xc3, 0x82, 0x41, - 0x89, 0x76, 0x18, 0xb6, 0x05, 0x2b, 0xd0, 0x1f, 0xb2, 0x49, 0x90, 0x43, 0x63, 0xb7, 0xeb, 0x38, - 0x9f, 0xaf, 0x98, 0xe3, 0xe3, 0xff, 0x43, 0x2c, 0x69, 0x30, 0x85, 0xa1, 0xea, 0xfe, 0xbb, 0x44, - 0x61, 0xa8, 0xfa, 0x93, 0x73, 0x29, 0xfd, 0x39, 0xeb, 0xb3, 0x26, 0x81, 0xa7, 0x0f, 0x93, 0x66, - 0x40, 0xd6, 0xdd, 0x3b, 0xc9, 0x87, 0x49, 0x99, 0x95, 0x62, 0x01, 0x45, 0x6f, 0xc1, 0x70, 0x55, - 0x6a, 0x8a, 0xe2, 0x1d, 0x7e, 0xb6, 0xad, 0xd6, 0x52, 0x29, 0xb8, 0xb9, 0xa4, 0x73, 0x5e, 0xab, - 0x8f, 0x0d, 0x6a, 0xa6, 0x15, 0x58, 0xb1, 0x93, 0x15, 0x58, 0x4c, 0x37, 0xdf, 0x26, 0xea, 0x27, - 0x2d, 0xe8, 0xe3, 0x1a, 0x82, 0xee, 0x14, 0x34, 0x9a, 0xbe, 0x3f, 0x1e, 0xbb, 0x1b, 0xb4, 0x50, - 0x70, 0x36, 0x68, 0x05, 0x06, 0xd9, 0x0f, 0xa6, 0xe1, 0x28, 0xe6, 0xfb, 0x8c, 0xf1, 0x56, 0xf5, - 0x0e, 0xde, 0x90, 0xd5, 0x70, 0x4c, 0xc1, 0xfe, 0xd1, 0x22, 0x3d, 0xdd, 0x62, 0x54, 0xe3, 0xd2, - 0xb7, 0x1e, 0xdc, 0xa5, 0x5f, 0x78, 0x50, 0x97, 0xfe, 0x06, 0x8c, 0x55, 0x35, 0xeb, 0x80, 0x78, - 0x26, 0xcf, 0xb5, 0x5d, 0x24, 0x9a, 0x21, 0x01, 0x97, 0xa1, 0xce, 0x9b, 0x44, 0x70, 0x92, 0x2a, - 0xfa, 0x38, 0x0c, 0xf3, 0x79, 0x16, 0xad, 0x70, 0x43, 0xba, 0xc7, 0xf2, 0xd7, 0x8b, 0xde, 0x04, - 0x97, 0xb9, 0x6b, 0xd5, 0xb1, 0x41, 0xcc, 0xfe, 0x6b, 0x0b, 0xd0, 0x62, 0x73, 0x93, 0x34, 0x48, - 0xe0, 0xd4, 0x63, 0x25, 0xdf, 0x17, 0x2c, 0x98, 0x22, 0xa9, 0xe2, 0x79, 0xbf, 0xd1, 0x10, 0x4f, - 0xfa, 0x1c, 0xa9, 0xd3, 0x62, 0x4e, 0x9d, 0x98, 0xad, 0xcf, 0xc3, 0xc0, 0xb9, 0xed, 0xa1, 0x15, - 0x98, 0xe4, 0xb7, 0xa4, 0x02, 0x68, 0xb6, 0x76, 0x0f, 0x09, 0xc2, 0x93, 0x6b, 0x69, 0x14, 0x9c, - 0x55, 0xcf, 0xfe, 0x8d, 0x11, 0xc8, 0xed, 0xc5, 0xfb, 0xda, 0xcd, 0xf7, 0xb5, 0x9b, 0xef, 0x6b, - 0x37, 0xdf, 0xd7, 0x6e, 0xbe, 0xaf, 0xdd, 0x7c, 0x5f, 0xbb, 0xf9, 0x2e, 0xd5, 0x6e, 0xfe, 0x7d, - 0x0b, 0x8e, 0xaa, 0xeb, 0xcb, 0x78, 0xb0, 0x7f, 0x06, 0x26, 0xf9, 0x76, 0x9b, 0xaf, 0x3b, 0x6e, - 0x63, 0x8d, 0x34, 0x9a, 0x75, 0x27, 0x92, 0x36, 0x4c, 0x17, 0x32, 0x57, 0x6e, 0xc2, 0x51, 0xc2, - 0xa8, 0xc8, 0x3d, 0xce, 0x32, 0x00, 0x38, 0xab, 0x19, 0xfb, 0x57, 0x07, 0xa0, 0x77, 0x71, 0x9b, - 0x78, 0xd1, 0x21, 0x3c, 0x6d, 0xaa, 0x30, 0xea, 0x7a, 0xdb, 0x7e, 0x7d, 0x9b, 0xd4, 0x38, 0xfc, - 0x20, 0x2f, 0xf0, 0x63, 0x82, 0xf4, 0xe8, 0xb2, 0x41, 0x02, 0x27, 0x48, 0x3e, 0x08, 0x1d, 0xd1, - 0x25, 0xe8, 0xe3, 0x97, 0x8f, 0x50, 0x10, 0x65, 0x9e, 0xd9, 0x6c, 0x10, 0xc5, 0x95, 0x1a, 0xeb, - 0xaf, 0xf8, 0xe5, 0x26, 0xaa, 0xa3, 0x4f, 0xc3, 0xe8, 0xba, 0x1b, 0x84, 0xd1, 0x9a, 0xdb, 0xa0, - 0x57, 0x43, 0xa3, 0x79, 0x0f, 0x3a, 0x21, 0x35, 0x0e, 0x4b, 0x06, 0x25, 0x9c, 0xa0, 0x8c, 0x36, - 0x60, 0xa4, 0xee, 0xe8, 0x4d, 0xf5, 0x1f, 0xb8, 0x29, 0x75, 0x3b, 0x5c, 0xd5, 0x09, 0x61, 0x93, - 0x2e, 0xdd, 0x4e, 0x55, 0xa6, 0xd6, 0x18, 0x60, 0xe2, 0x0c, 0xb5, 0x9d, 0xb8, 0x3e, 0x83, 0xc3, - 0x28, 0x83, 0xc6, 0xdc, 0x0d, 0x06, 0x4d, 0x06, 0x4d, 0x73, 0x2a, 0xf8, 0x14, 0x0c, 0x12, 0x3a, - 0x84, 0x94, 0xb0, 0xb8, 0x60, 0xce, 0x77, 0xd7, 0xd7, 0x15, 0xb7, 0x1a, 0xf8, 0xa6, 0x36, 0x6e, - 0x51, 0x52, 0xc2, 0x31, 0x51, 0x34, 0x0f, 0x7d, 0x21, 0x09, 0x5c, 0x25, 0xf1, 0x6f, 0x33, 0x8d, - 0x0c, 0x8d, 0xbb, 0x34, 0xf2, 0xdf, 0x58, 0x54, 0xa5, 0xcb, 0xcb, 0x61, 0xa2, 0x58, 0x76, 0x19, - 0x68, 0xcb, 0x6b, 0x96, 0x95, 0x62, 0x01, 0x45, 0xaf, 0x43, 0x7f, 0x40, 0xea, 0x4c, 0xdd, 0x3b, - 0xd2, 0xfd, 0x22, 0xe7, 0xda, 0x63, 0x5e, 0x0f, 0x4b, 0x02, 0xe8, 0x0a, 0xa0, 0x80, 0x50, 0x06, - 0xcf, 0xf5, 0x36, 0x94, 0x11, 0xbe, 0x38, 0x68, 0x15, 0x23, 0x8d, 0x63, 0x0c, 0xe9, 0xcd, 0x8a, - 0x33, 0xaa, 0xa1, 0x4b, 0x30, 0xa1, 0x4a, 0x97, 0xbd, 0x30, 0x72, 0xe8, 0x01, 0x37, 0xc6, 0x68, - 0x29, 0xf9, 0x0a, 0x4e, 0x22, 0xe0, 0x74, 0x1d, 0xfb, 0xe7, 0x2d, 0xe0, 0xe3, 0x7c, 0x08, 0x52, - 0x85, 0x57, 0x4d, 0xa9, 0xc2, 0x89, 0xdc, 0x99, 0xcb, 0x91, 0x28, 0xfc, 0xbc, 0x05, 0x43, 0xda, - 0xcc, 0xc6, 0x6b, 0xd6, 0x6a, 0xb3, 0x66, 0x5b, 0x30, 0x4e, 0x57, 0xfa, 0xb5, 0x5b, 0x21, 0x09, - 0xb6, 0x49, 0x8d, 0x2d, 0xcc, 0xc2, 0xbd, 0x2d, 0x4c, 0x65, 0xf0, 0x7b, 0x35, 0x41, 0x10, 0xa7, - 0x9a, 0xb0, 0x3f, 0x25, 0xbb, 0xaa, 0xec, 0xa3, 0xab, 0x6a, 0xce, 0x13, 0xf6, 0xd1, 0x6a, 0x56, - 0x71, 0x8c, 0x43, 0xb7, 0xda, 0xa6, 0x1f, 0x46, 0x49, 0xfb, 0xe8, 0xcb, 0x7e, 0x18, 0x61, 0x06, - 0xb1, 0x9f, 0x03, 0x58, 0xbc, 0x43, 0xaa, 0x7c, 0xc5, 0xea, 0x8f, 0x1e, 0x2b, 0xff, 0xd1, 0x63, - 0xff, 0x91, 0x05, 0xa3, 0x4b, 0xf3, 0xc6, 0xcd, 0x35, 0x03, 0xc0, 0x5f, 0x6a, 0x37, 0x6f, 0xae, - 0x4a, 0x23, 0x1d, 0x6e, 0xa7, 0xa0, 0x4a, 0xb1, 0x86, 0x81, 0x4e, 0x40, 0xb1, 0xde, 0xf2, 0x84, - 0xd8, 0xb3, 0x9f, 0x5e, 0x8f, 0x57, 0x5b, 0x1e, 0xa6, 0x65, 0x9a, 0x27, 0x5b, 0xb1, 0x6b, 0x4f, - 0xb6, 0x8e, 0x01, 0x75, 0x50, 0x09, 0x7a, 0x6f, 0xdf, 0x76, 0x6b, 0x3c, 0x4e, 0x80, 0x30, 0x20, - 0xba, 0x79, 0x73, 0x79, 0x21, 0xc4, 0xbc, 0xdc, 0xfe, 0x62, 0x11, 0xa6, 0x97, 0xea, 0xe4, 0xce, - 0x3b, 0x8c, 0x95, 0xd0, 0xad, 0x1f, 0xde, 0xc1, 0x04, 0x48, 0x07, 0xf5, 0xb5, 0xec, 0x3c, 0x1e, - 0xeb, 0xd0, 0xcf, 0xcd, 0x83, 0x65, 0xe4, 0x84, 0x4c, 0xa5, 0x6c, 0xfe, 0x80, 0xcc, 0x70, 0x33, - 0x63, 0xa1, 0x94, 0x55, 0x17, 0xa6, 0x28, 0xc5, 0x92, 0xf8, 0xf4, 0x4b, 0x30, 0xac, 0x63, 0x1e, - 0xc8, 0xeb, 0xf9, 0x7b, 0x8b, 0x30, 0x4e, 0x7b, 0xf0, 0x40, 0x27, 0xe2, 0x7a, 0x7a, 0x22, 0xee, - 0xb7, 0xe7, 0x6b, 0xe7, 0xd9, 0x78, 0x2b, 0x39, 0x1b, 0x17, 0xf2, 0x66, 0xe3, 0xb0, 0xe7, 0xe0, - 0xfb, 0x2c, 0x98, 0x5c, 0xaa, 0xfb, 0xd5, 0xad, 0x84, 0x77, 0xea, 0x0b, 0x30, 0x44, 0x8f, 0xe3, - 0xd0, 0x08, 0xd4, 0x62, 0x84, 0xee, 0x11, 0x20, 0xac, 0xe3, 0x69, 0xd5, 0xae, 0x5f, 0x5f, 0x5e, - 0xc8, 0x8a, 0xf8, 0x23, 0x40, 0x58, 0xc7, 0xb3, 0xff, 0xc0, 0x82, 0x53, 0x97, 0xe6, 0x17, 0xe3, - 0xa5, 0x98, 0x0a, 0x3a, 0x74, 0x16, 0xfa, 0x9a, 0x35, 0xad, 0x2b, 0xb1, 0x58, 0x78, 0x81, 0xf5, - 0x42, 0x40, 0xdf, 0x2d, 0xf1, 0xbd, 0xae, 0x03, 0x5c, 0xc2, 0xe5, 0x79, 0x71, 0xee, 0x4a, 0x2d, - 0x90, 0x95, 0xab, 0x05, 0x7a, 0x0c, 0xfa, 0xe9, 0xbd, 0xe0, 0x56, 0x65, 0xbf, 0xb9, 0xd9, 0x05, - 0x2f, 0xc2, 0x12, 0x66, 0xff, 0x9c, 0x05, 0x93, 0x97, 0xdc, 0x88, 0x5e, 0xda, 0xc9, 0xa8, 0x3a, - 0xf4, 0xd6, 0x0e, 0xdd, 0xc8, 0x0f, 0x76, 0x92, 0x51, 0x75, 0xb0, 0x82, 0x60, 0x0d, 0x8b, 0x7f, - 0xd0, 0xb6, 0xcb, 0xfc, 0x5d, 0x0a, 0xa6, 0xde, 0x0d, 0x8b, 0x72, 0xac, 0x30, 0xe8, 0x78, 0xd5, - 0xdc, 0x80, 0x89, 0x2c, 0x77, 0xc4, 0xc1, 0xad, 0xc6, 0x6b, 0x41, 0x02, 0x70, 0x8c, 0x63, 0xff, - 0xa5, 0x05, 0xa5, 0x4b, 0xdc, 0x6b, 0x77, 0x3d, 0xcc, 0x39, 0x74, 0x9f, 0x83, 0x41, 0x22, 0x15, - 0x04, 0xa2, 0xd7, 0x8a, 0x11, 0x55, 0x9a, 0x03, 0x1e, 0xdc, 0x47, 0xe1, 0x75, 0xe1, 0x42, 0x7f, - 0x30, 0x1f, 0xe8, 0x25, 0x40, 0x44, 0x6f, 0x4b, 0x8f, 0x76, 0xc4, 0xc2, 0xa6, 0x2c, 0xa6, 0xa0, - 0x38, 0xa3, 0x86, 0xfd, 0x13, 0x16, 0x1c, 0x55, 0x1f, 0xfc, 0xae, 0xfb, 0x4c, 0xfb, 0xab, 0x05, - 0x18, 0xb9, 0xbc, 0xb6, 0x56, 0xbe, 0x44, 0x22, 0x6d, 0x55, 0xb6, 0x57, 0xfb, 0x63, 0x4d, 0x7b, - 0xd9, 0xee, 0x8d, 0xd8, 0x8a, 0xdc, 0xfa, 0x0c, 0x8f, 0xe1, 0x37, 0xb3, 0xec, 0x45, 0xd7, 0x82, - 0x4a, 0x14, 0xb8, 0xde, 0x46, 0xe6, 0x4a, 0x97, 0x3c, 0x4b, 0x31, 0x8f, 0x67, 0x41, 0xcf, 0x41, - 0x1f, 0x0b, 0x22, 0x28, 0x27, 0xe1, 0x21, 0xf5, 0xc4, 0x62, 0xa5, 0xfb, 0xbb, 0xa5, 0xc1, 0xeb, - 0x78, 0x99, 0xff, 0xc1, 0x02, 0x15, 0x5d, 0x87, 0xa1, 0xcd, 0x28, 0x6a, 0x5e, 0x26, 0x4e, 0x8d, - 0x04, 0xf2, 0x94, 0x3d, 0x9d, 0x75, 0xca, 0xd2, 0x41, 0xe0, 0x68, 0xf1, 0xc1, 0x14, 0x97, 0x85, - 0x58, 0xa7, 0x63, 0x57, 0x00, 0x62, 0xd8, 0x7d, 0x52, 0xdc, 0xd8, 0x6b, 0x30, 0x48, 0x3f, 0x77, - 0xb6, 0xee, 0x3a, 0xed, 0x55, 0xe3, 0x4f, 0xc1, 0xa0, 0x54, 0x7c, 0x87, 0x22, 0xc4, 0x07, 0xbb, - 0x91, 0xa4, 0x5e, 0x3c, 0xc4, 0x31, 0xdc, 0x7e, 0x14, 0x84, 0x05, 0x70, 0x3b, 0x92, 0xf6, 0x3a, - 0x1c, 0x61, 0xa6, 0xcc, 0x4e, 0xb4, 0x69, 0xac, 0xd1, 0xce, 0x8b, 0xe1, 0x69, 0xf1, 0xae, 0xe3, - 0x5f, 0x36, 0xa5, 0xb9, 0x90, 0x0f, 0x4b, 0x8a, 0xf1, 0x1b, 0xcf, 0xfe, 0x8b, 0x1e, 0x78, 0x68, - 0xb9, 0x92, 0x1f, 0x9b, 0xea, 0x22, 0x0c, 0x73, 0x76, 0x91, 0x2e, 0x0d, 0xa7, 0x2e, 0xda, 0x55, - 0x12, 0xd0, 0x35, 0x0d, 0x86, 0x0d, 0x4c, 0x74, 0x0a, 0x8a, 0xee, 0xdb, 0x5e, 0xd2, 0xc1, 0x72, - 0xf9, 0x8d, 0x55, 0x4c, 0xcb, 0x29, 0x98, 0x72, 0x9e, 0xfc, 0x48, 0x57, 0x60, 0xc5, 0x7d, 0xbe, - 0x0a, 0xa3, 0x6e, 0x58, 0x0d, 0xdd, 0x65, 0x8f, 0xee, 0x53, 0x6d, 0xa7, 0x2b, 0x99, 0x03, 0xed, - 0xb4, 0x82, 0xe2, 0x04, 0xb6, 0x76, 0xbf, 0xf4, 0x76, 0xcd, 0xbd, 0x76, 0x8c, 0x8c, 0x41, 0x8f, - 0xff, 0x26, 0xfb, 0xba, 0x90, 0x89, 0xe0, 0xc5, 0xf1, 0xcf, 0x3f, 0x38, 0xc4, 0x12, 0x46, 0x1f, - 0x74, 0xd5, 0x4d, 0xa7, 0x39, 0xdb, 0x8a, 0x36, 0x17, 0xdc, 0xb0, 0xea, 0x6f, 0x93, 0x60, 0x87, - 0xbd, 0xc5, 0x07, 0xe2, 0x07, 0x9d, 0x02, 0xcc, 0x5f, 0x9e, 0x2d, 0x53, 0x4c, 0x9c, 0xae, 0x83, - 0x66, 0x61, 0x4c, 0x16, 0x56, 0x48, 0xc8, 0xae, 0x80, 0x21, 0x46, 0x46, 0xb9, 0x3c, 0x8a, 0x62, - 0x45, 0x24, 0x89, 0x6f, 0x32, 0xb8, 0x70, 0x3f, 0x18, 0xdc, 0x17, 0x61, 0xc4, 0xf5, 0xdc, 0xc8, - 0x75, 0x22, 0x9f, 0xeb, 0x8f, 0xf8, 0xb3, 0x9b, 0x09, 0x98, 0x97, 0x75, 0x00, 0x36, 0xf1, 0xec, - 0xff, 0xd6, 0x03, 0x13, 0x6c, 0xda, 0xde, 0x5f, 0x61, 0xef, 0xa5, 0x15, 0x76, 0x3d, 0xbd, 0xc2, - 0xee, 0x07, 0xe7, 0x7e, 0xcf, 0xcb, 0xec, 0x73, 0x16, 0x4c, 0x30, 0x19, 0xb7, 0xb1, 0xcc, 0xce, - 0xc3, 0x60, 0x60, 0x78, 0xa3, 0x0e, 0xea, 0x4a, 0x2d, 0xe9, 0x58, 0x1a, 0xe3, 0xa0, 0xd7, 0x00, - 0x9a, 0xb1, 0x0c, 0xbd, 0x60, 0x84, 0x10, 0x85, 0x5c, 0xf1, 0xb9, 0x56, 0xc7, 0xfe, 0x34, 0x0c, - 0x2a, 0x77, 0x53, 0xe9, 0x6f, 0x6e, 0xe5, 0xf8, 0x9b, 0x77, 0x66, 0x23, 0xa4, 0x6d, 0x5c, 0x31, - 0xd3, 0x36, 0xee, 0xcb, 0x16, 0xc4, 0x1a, 0x0e, 0xf4, 0x06, 0x0c, 0x36, 0x7d, 0x66, 0x10, 0x1d, - 0x48, 0x2f, 0x83, 0x47, 0xdb, 0xaa, 0x48, 0x78, 0x9c, 0xc0, 0x80, 0x4f, 0x47, 0x59, 0x56, 0xc5, - 0x31, 0x15, 0x74, 0x05, 0xfa, 0x9b, 0x01, 0xa9, 0x44, 0x2c, 0x88, 0x55, 0xf7, 0x04, 0xf9, 0xf2, - 0xe5, 0x15, 0xb1, 0xa4, 0x60, 0xff, 0x62, 0x01, 0xc6, 0x93, 0xa8, 0xe8, 0x15, 0xe8, 0x21, 0x77, - 0x48, 0x55, 0xf4, 0x37, 0x93, 0x27, 0x88, 0x65, 0x24, 0x7c, 0x00, 0xe8, 0x7f, 0xcc, 0x6a, 0xa1, - 0xcb, 0xd0, 0x4f, 0x19, 0x82, 0x4b, 0x2a, 0x60, 0xe3, 0xc3, 0x79, 0x4c, 0x85, 0xe2, 0xac, 0x78, - 0xe7, 0x44, 0x11, 0x96, 0xd5, 0x99, 0x41, 0x5a, 0xb5, 0x59, 0xa1, 0x6f, 0xad, 0xa8, 0x9d, 0x48, - 0x60, 0x6d, 0xbe, 0xcc, 0x91, 0x04, 0x35, 0x6e, 0x90, 0x26, 0x0b, 0x71, 0x4c, 0x04, 0xbd, 0x06, - 0xbd, 0x61, 0x9d, 0x90, 0xa6, 0xb0, 0x38, 0xc8, 0x94, 0x72, 0x56, 0x28, 0x82, 0xa0, 0xc4, 0xa4, - 0x22, 0xac, 0x00, 0xf3, 0x8a, 0xf6, 0x2f, 0x5b, 0x00, 0xdc, 0x82, 0xcf, 0xf1, 0x36, 0xc8, 0x21, - 0x28, 0x06, 0x16, 0xa0, 0x27, 0x6c, 0x92, 0x6a, 0x3b, 0x6b, 0xff, 0xb8, 0x3f, 0x95, 0x26, 0xa9, - 0xc6, 0x6b, 0x96, 0xfe, 0xc3, 0xac, 0xb6, 0xfd, 0xfd, 0x00, 0xa3, 0x31, 0xda, 0x72, 0x44, 0x1a, - 0xe8, 0x19, 0x23, 0xca, 0xcd, 0x89, 0x44, 0x94, 0x9b, 0x41, 0x86, 0xad, 0xc9, 0xa0, 0x3f, 0x0d, - 0xc5, 0x86, 0x73, 0x47, 0x08, 0x19, 0x9f, 0x6a, 0xdf, 0x0d, 0x4a, 0x7f, 0x66, 0xc5, 0xb9, 0xc3, - 0xdf, 0xe1, 0x4f, 0xc9, 0x3d, 0xb6, 0xe2, 0xdc, 0xe9, 0x68, 0x91, 0x4e, 0x1b, 0x61, 0x6d, 0xb9, - 0x9e, 0x30, 0x4e, 0xeb, 0xaa, 0x2d, 0xd7, 0x4b, 0xb6, 0xe5, 0x7a, 0x5d, 0xb4, 0xe5, 0x7a, 0xe8, - 0x2e, 0xf4, 0x0b, 0xdb, 0x51, 0x11, 0x7e, 0xef, 0x7c, 0x17, 0xed, 0x09, 0xd3, 0x53, 0xde, 0xe6, - 0x79, 0x29, 0x67, 0x10, 0xa5, 0x1d, 0xdb, 0x95, 0x0d, 0xa2, 0x7f, 0x60, 0xc1, 0xa8, 0xf8, 0x8d, - 0xc9, 0xdb, 0x2d, 0x12, 0x46, 0x82, 0x0f, 0xff, 0x50, 0xf7, 0x7d, 0x10, 0x15, 0x79, 0x57, 0x3e, - 0x24, 0xaf, 0x4c, 0x13, 0xd8, 0xb1, 0x47, 0x89, 0x5e, 0xa0, 0x5f, 0xb4, 0xe0, 0x48, 0xc3, 0xb9, - 0xc3, 0x5b, 0xe4, 0x65, 0xd8, 0x89, 0x5c, 0x5f, 0xd8, 0x60, 0xbc, 0xd2, 0xdd, 0xf4, 0xa7, 0xaa, - 0xf3, 0x4e, 0x4a, 0x85, 0xeb, 0x91, 0x2c, 0x94, 0x8e, 0x5d, 0xcd, 0xec, 0xd7, 0xf4, 0x3a, 0x0c, - 0xc8, 0xf5, 0xf6, 0x20, 0x0d, 0xe3, 0x59, 0x3b, 0x62, 0xad, 0x3d, 0xd0, 0x76, 0x3e, 0x0d, 0xc3, - 0xfa, 0x1a, 0x7b, 0xa0, 0x6d, 0xbd, 0x0d, 0x93, 0x19, 0x6b, 0xe9, 0x81, 0x36, 0x79, 0x1b, 0x4e, - 0xe4, 0xae, 0x8f, 0x07, 0xea, 0xd8, 0xf0, 0x55, 0x4b, 0x3f, 0x07, 0x0f, 0x41, 0x3b, 0x33, 0x6f, - 0x6a, 0x67, 0x4e, 0xb7, 0xdf, 0x39, 0x39, 0x2a, 0x9a, 0xb7, 0xf4, 0x4e, 0xd3, 0x53, 0x1d, 0xbd, - 0x0e, 0x7d, 0x75, 0x5a, 0x22, 0x2d, 0x90, 0xed, 0xce, 0x3b, 0x32, 0xe6, 0x8b, 0x59, 0x79, 0x88, - 0x05, 0x05, 0xfb, 0x4b, 0x16, 0x64, 0xb8, 0x66, 0x50, 0x3e, 0xa9, 0xe5, 0xd6, 0xd8, 0x90, 0x14, - 0x63, 0x3e, 0x49, 0x05, 0x81, 0x39, 0x05, 0xc5, 0x0d, 0xb7, 0x26, 0x3c, 0x8b, 0x15, 0xf8, 0x12, - 0x05, 0x6f, 0xb8, 0x35, 0xb4, 0x04, 0x28, 0x6c, 0x35, 0x9b, 0x75, 0x66, 0xb6, 0xe4, 0xd4, 0x2f, - 0x05, 0x7e, 0xab, 0xc9, 0xcd, 0x8d, 0x8b, 0x5c, 0x48, 0x54, 0x49, 0x41, 0x71, 0x46, 0x0d, 0xfb, - 0xd7, 0x2c, 0xe8, 0x39, 0x84, 0x69, 0xc2, 0xe6, 0x34, 0x3d, 0x93, 0x4b, 0x5a, 0x64, 0x6d, 0x98, - 0xc1, 0xce, 0xed, 0xc5, 0x3b, 0x11, 0xf1, 0x42, 0xc6, 0x70, 0x64, 0xce, 0xda, 0xae, 0x05, 0x93, - 0x57, 0x7d, 0xa7, 0x36, 0xe7, 0xd4, 0x1d, 0xaf, 0x4a, 0x82, 0x65, 0x6f, 0xe3, 0x40, 0xb6, 0xfd, - 0x85, 0x8e, 0xb6, 0xfd, 0x17, 0xa1, 0xcf, 0x6d, 0x6a, 0x61, 0xdf, 0xcf, 0xd0, 0xd9, 0x5d, 0x2e, - 0x8b, 0x88, 0xef, 0xc8, 0x68, 0x9c, 0x95, 0x62, 0x81, 0x4f, 0x97, 0x25, 0x37, 0xaa, 0xeb, 0xc9, - 0x5f, 0x96, 0xf4, 0xad, 0x93, 0x0c, 0x67, 0x66, 0x98, 0x7f, 0x6f, 0x82, 0xd1, 0x84, 0xf0, 0x60, - 0xc4, 0xd0, 0xef, 0xf2, 0x2f, 0x15, 0x6b, 0xf3, 0xf1, 0xec, 0x37, 0x48, 0x6a, 0x60, 0x34, 0xdf, - 0x3c, 0x5e, 0x80, 0x25, 0x21, 0xfb, 0x22, 0x64, 0x86, 0x9f, 0xe9, 0x2c, 0x5f, 0xb2, 0x3f, 0x06, - 0x13, 0xac, 0xe6, 0x01, 0x65, 0x37, 0x76, 0x42, 0x2a, 0x9e, 0x11, 0xc1, 0xd7, 0xfe, 0xcf, 0x16, - 0xa0, 0x15, 0xbf, 0xe6, 0xae, 0xef, 0x08, 0xe2, 0xfc, 0xfb, 0xdf, 0x86, 0x12, 0x7f, 0x1c, 0x27, - 0xa3, 0xdc, 0xce, 0xd7, 0x9d, 0x30, 0xd4, 0x24, 0xf2, 0x8f, 0x8b, 0x76, 0x4b, 0x6b, 0xed, 0xd1, - 0x71, 0x27, 0x7a, 0xe8, 0x8d, 0x44, 0xd0, 0xc1, 0x0f, 0xa7, 0x82, 0x0e, 0x3e, 0x9e, 0x69, 0x17, - 0x93, 0xee, 0xbd, 0x0c, 0x46, 0x68, 0x7f, 0xde, 0x82, 0xb1, 0xd5, 0x44, 0xd4, 0xd6, 0xb3, 0xcc, - 0x48, 0x20, 0x43, 0xd3, 0x54, 0x61, 0xa5, 0x58, 0x40, 0xef, 0xbb, 0x24, 0xf6, 0xef, 0x2c, 0x88, - 0xc3, 0x5d, 0x1d, 0x02, 0xcb, 0x3d, 0x6f, 0xb0, 0xdc, 0x99, 0xcf, 0x17, 0xd5, 0x9d, 0x3c, 0x8e, - 0x1b, 0x5d, 0x51, 0x73, 0xd2, 0xe6, 0xe5, 0x12, 0x93, 0xe1, 0xfb, 0x6c, 0xd4, 0x9c, 0x38, 0x35, - 0x1b, 0x5f, 0x2f, 0x00, 0x52, 0xb8, 0x5d, 0x07, 0xaa, 0x4c, 0xd7, 0xb8, 0x3f, 0x81, 0x2a, 0xb7, - 0x01, 0x31, 0x33, 0x97, 0xc0, 0xf1, 0x42, 0x4e, 0xd6, 0x15, 0xb2, 0xe7, 0x83, 0xd9, 0xd0, 0x4c, - 0x4b, 0xcf, 0xd5, 0xab, 0x29, 0x6a, 0x38, 0xa3, 0x05, 0xcd, 0x7c, 0xa9, 0xb7, 0x5b, 0xf3, 0xa5, - 0xbe, 0x0e, 0x2e, 0xd8, 0x5f, 0xb1, 0x60, 0x44, 0x0d, 0xd3, 0xbb, 0xc4, 0x05, 0x44, 0xf5, 0x27, - 0xe7, 0x5e, 0x29, 0x6b, 0x5d, 0x66, 0xcc, 0xc0, 0x77, 0x30, 0x57, 0x7a, 0xa7, 0xee, 0xde, 0x25, - 0x2a, 0x9e, 0x72, 0x49, 0xb8, 0xc6, 0x8b, 0xd2, 0xfd, 0xdd, 0xd2, 0x88, 0xfa, 0xc7, 0x23, 0xb8, - 0xc6, 0x55, 0xec, 0x9f, 0xa6, 0x9b, 0xdd, 0x5c, 0x8a, 0xe8, 0x05, 0xe8, 0x6d, 0x6e, 0x3a, 0x21, - 0x49, 0xb8, 0xca, 0xf5, 0x96, 0x69, 0xe1, 0xfe, 0x6e, 0x69, 0x54, 0x55, 0x60, 0x25, 0x98, 0x63, - 0x77, 0x1f, 0xfe, 0x33, 0xbd, 0x38, 0x3b, 0x86, 0xff, 0xfc, 0x6b, 0x0b, 0x7a, 0x56, 0xe9, 0xed, - 0xf5, 0xe0, 0x8f, 0x80, 0x57, 0x8d, 0x23, 0xe0, 0x64, 0x5e, 0x66, 0xa1, 0xdc, 0xdd, 0xbf, 0x94, - 0xd8, 0xfd, 0xa7, 0x73, 0x29, 0xb4, 0xdf, 0xf8, 0x0d, 0x18, 0x62, 0xf9, 0x8a, 0x84, 0x5b, 0xe0, - 0x73, 0xc6, 0x86, 0x2f, 0x25, 0x36, 0xfc, 0x98, 0x86, 0xaa, 0xed, 0xf4, 0x27, 0xa0, 0x5f, 0xf8, - 0x99, 0x25, 0x23, 0x12, 0x08, 0x5c, 0x2c, 0xe1, 0xf6, 0x4f, 0x16, 0xc1, 0xc8, 0x8f, 0x84, 0x7e, - 0xc3, 0x82, 0x99, 0x80, 0xdb, 0x9f, 0xd7, 0x16, 0x5a, 0x81, 0xeb, 0x6d, 0x54, 0xaa, 0x9b, 0xa4, - 0xd6, 0xaa, 0xbb, 0xde, 0xc6, 0xf2, 0x86, 0xe7, 0xab, 0xe2, 0xc5, 0x3b, 0xa4, 0xda, 0x62, 0xba, - 0xe1, 0x0e, 0xc9, 0x98, 0x94, 0x1f, 0xc7, 0xb3, 0x7b, 0xbb, 0xa5, 0x19, 0x7c, 0x20, 0xda, 0xf8, - 0x80, 0x7d, 0x41, 0x7f, 0x60, 0xc1, 0x79, 0x9e, 0xa7, 0xa7, 0xfb, 0xfe, 0xb7, 0x91, 0x70, 0x94, - 0x25, 0xa9, 0x98, 0xc8, 0x1a, 0x09, 0x1a, 0x73, 0x2f, 0x8a, 0x01, 0x3d, 0x5f, 0x3e, 0x58, 0x5b, - 0xf8, 0xa0, 0x9d, 0xb3, 0xff, 0x55, 0x11, 0x46, 0x44, 0x98, 0x48, 0x71, 0x07, 0xbc, 0x60, 0x2c, - 0x89, 0x87, 0x13, 0x4b, 0x62, 0xc2, 0x40, 0xbe, 0x3f, 0xc7, 0x7f, 0x08, 0x13, 0xf4, 0x70, 0xbe, - 0x4c, 0x9c, 0x20, 0xba, 0x45, 0x1c, 0x6e, 0x95, 0x58, 0x3c, 0xf0, 0xe9, 0xaf, 0xc4, 0xe3, 0x57, - 0x93, 0xc4, 0x70, 0x9a, 0xfe, 0x7b, 0xe9, 0xce, 0xf1, 0x60, 0x3c, 0x15, 0xe9, 0xf3, 0x4d, 0x18, - 0x54, 0x4e, 0x52, 0xe2, 0xd0, 0x69, 0x1f, 0x30, 0x37, 0x49, 0x81, 0x0b, 0x3d, 0x63, 0x07, 0xbd, - 0x98, 0x9c, 0xfd, 0x4b, 0x05, 0xa3, 0x41, 0x3e, 0x89, 0xab, 0x30, 0xe0, 0x84, 0x2c, 0x88, 0x77, - 0xad, 0x9d, 0x5c, 0x3a, 0xd5, 0x0c, 0x73, 0x54, 0x9b, 0x15, 0x35, 0xb1, 0xa2, 0x81, 0x2e, 0x73, - 0xdb, 0xcf, 0x6d, 0xd2, 0x4e, 0x28, 0x9d, 0xa2, 0x06, 0xd2, 0x3a, 0x74, 0x9b, 0x60, 0x51, 0x1f, - 0x7d, 0x82, 0x1b, 0xe7, 0x5e, 0xf1, 0xfc, 0xdb, 0xde, 0x25, 0xdf, 0x97, 0x21, 0x81, 0xba, 0x23, - 0x38, 0x21, 0x4d, 0x72, 0x55, 0x75, 0x6c, 0x52, 0xeb, 0x2e, 0x74, 0xf6, 0x67, 0x80, 0xe5, 0x25, - 0x31, 0x63, 0x12, 0x84, 0x88, 0xc0, 0x98, 0x88, 0x41, 0x2a, 0xcb, 0xc4, 0xd8, 0x65, 0x3e, 0xbf, - 0xcd, 0xda, 0xb1, 0x1e, 0xe7, 0x8a, 0x49, 0x02, 0x27, 0x69, 0xda, 0x9b, 0xfc, 0x10, 0x5e, 0x22, - 0x4e, 0xd4, 0x0a, 0x48, 0x88, 0x3e, 0x0a, 0x53, 0xe9, 0x97, 0xb1, 0x50, 0x87, 0x58, 0x8c, 0x7b, - 0x3e, 0xb9, 0xb7, 0x5b, 0x9a, 0xaa, 0xe4, 0xe0, 0xe0, 0xdc, 0xda, 0xf6, 0xcf, 0x5a, 0xc0, 0x3c, - 0xc1, 0x0f, 0x81, 0xf3, 0xf9, 0x88, 0xc9, 0xf9, 0x4c, 0xe5, 0x4d, 0x67, 0x0e, 0xd3, 0xf3, 0x3c, - 0x5f, 0xc3, 0xe5, 0xc0, 0xbf, 0xb3, 0x23, 0x6c, 0xb7, 0x3a, 0x3f, 0xe3, 0xec, 0x2f, 0x5a, 0xc0, - 0x92, 0xf8, 0x60, 0xfe, 0x6a, 0x97, 0x0a, 0x8e, 0xce, 0x66, 0x09, 0x1f, 0x85, 0x81, 0x75, 0x31, - 0xfc, 0x19, 0x42, 0x27, 0xa3, 0xc3, 0x26, 0x6d, 0x39, 0x69, 0xc2, 0xa3, 0x53, 0xfc, 0xc3, 0x8a, - 0x9a, 0xfd, 0xcf, 0x2c, 0x98, 0xce, 0xaf, 0x86, 0xae, 0xc3, 0xf1, 0x80, 0x54, 0x5b, 0x41, 0x48, - 0xb7, 0x84, 0x78, 0x00, 0x09, 0xa7, 0x28, 0x3e, 0xd5, 0x0f, 0xed, 0xed, 0x96, 0x8e, 0xe3, 0x6c, - 0x14, 0x9c, 0x57, 0x17, 0xbd, 0x04, 0xa3, 0xad, 0x90, 0x73, 0x7e, 0x8c, 0xe9, 0x0a, 0x45, 0xa4, - 0x68, 0xe6, 0x37, 0x74, 0xdd, 0x80, 0xe0, 0x04, 0xa6, 0xfd, 0x5d, 0x7c, 0x39, 0xaa, 0x60, 0xd1, - 0x0d, 0x98, 0xf0, 0xb4, 0xff, 0xf4, 0x06, 0x94, 0x4f, 0xfd, 0x47, 0x3b, 0xdd, 0xfa, 0xec, 0xba, - 0xd4, 0x7c, 0xd5, 0x13, 0x64, 0x70, 0x9a, 0xb2, 0xfd, 0x53, 0x16, 0x1c, 0xd7, 0x11, 0x35, 0x77, - 0xb8, 0x4e, 0xba, 0xbc, 0x05, 0x18, 0xf0, 0x9b, 0x24, 0x70, 0x22, 0x3f, 0x10, 0xd7, 0xdc, 0x39, - 0xb9, 0x42, 0xaf, 0x89, 0xf2, 0x7d, 0x91, 0xbc, 0x46, 0x52, 0x97, 0xe5, 0x58, 0xd5, 0x44, 0x36, - 0xf4, 0x31, 0x01, 0x62, 0x28, 0x1c, 0x1f, 0xd9, 0xa1, 0xc5, 0xec, 0x53, 0x42, 0x2c, 0x20, 0xf6, - 0x5f, 0x58, 0x7c, 0x7d, 0xea, 0x5d, 0x47, 0x6f, 0xc3, 0x78, 0xc3, 0x89, 0xaa, 0x9b, 0x8b, 0x77, - 0x9a, 0x01, 0x57, 0xd1, 0xca, 0x71, 0x7a, 0xaa, 0xd3, 0x38, 0x69, 0x1f, 0x19, 0x1b, 0x48, 0xaf, - 0x24, 0x88, 0xe1, 0x14, 0x79, 0x74, 0x0b, 0x86, 0x58, 0x19, 0xf3, 0xe9, 0x0d, 0xdb, 0xf1, 0x32, - 0x79, 0xad, 0x29, 0x13, 0x9f, 0x95, 0x98, 0x0e, 0xd6, 0x89, 0xda, 0x5f, 0x2e, 0xf2, 0x43, 0x83, - 0xbd, 0x3d, 0x9e, 0x80, 0xfe, 0xa6, 0x5f, 0x9b, 0x5f, 0x5e, 0xc0, 0x62, 0x16, 0xd4, 0xbd, 0x57, - 0xe6, 0xc5, 0x58, 0xc2, 0xd1, 0x39, 0x18, 0x10, 0x3f, 0xa5, 0x4a, 0x9d, 0xed, 0x11, 0x81, 0x17, - 0x62, 0x05, 0x45, 0xcf, 0x02, 0x34, 0x03, 0x7f, 0xdb, 0xad, 0xb1, 0x48, 0x4c, 0x45, 0xd3, 0x3a, - 0xaf, 0xac, 0x20, 0x58, 0xc3, 0x42, 0x2f, 0xc3, 0x48, 0xcb, 0x0b, 0x39, 0xff, 0xa4, 0xc5, 0xbb, - 0x57, 0x76, 0x63, 0xd7, 0x75, 0x20, 0x36, 0x71, 0xd1, 0x2c, 0xf4, 0x45, 0x0e, 0xb3, 0x36, 0xeb, - 0xcd, 0x37, 0xa2, 0x5f, 0xa3, 0x18, 0x7a, 0x66, 0x39, 0x5a, 0x01, 0x8b, 0x8a, 0xe8, 0x4d, 0xe9, - 0x5e, 0xcf, 0x6f, 0x22, 0xe1, 0xbd, 0xd2, 0xdd, 0xad, 0xa5, 0x39, 0xd7, 0x0b, 0xaf, 0x18, 0x83, - 0x16, 0x7a, 0x09, 0x80, 0xdc, 0x89, 0x48, 0xe0, 0x39, 0x75, 0x65, 0x23, 0xaa, 0x18, 0x99, 0x05, - 0x7f, 0xd5, 0x8f, 0xae, 0x87, 0x64, 0x51, 0x61, 0x60, 0x0d, 0xdb, 0xfe, 0xfe, 0x21, 0x80, 0xf8, - 0xa1, 0x81, 0xee, 0xc2, 0x40, 0xd5, 0x69, 0x3a, 0x55, 0x9e, 0x36, 0xb5, 0x98, 0xe7, 0xf5, 0x1c, - 0xd7, 0x98, 0x99, 0x17, 0xe8, 0x5c, 0x79, 0x23, 0x43, 0x86, 0x0f, 0xc8, 0xe2, 0x8e, 0x0a, 0x1b, - 0xd5, 0x1e, 0xfa, 0x9c, 0x05, 0x43, 0x22, 0xd2, 0x11, 0x9b, 0xa1, 0x42, 0xbe, 0xbe, 0x4d, 0x6b, - 0x7f, 0x36, 0xae, 0xc1, 0xbb, 0xf0, 0x9c, 0x5c, 0xa1, 0x1a, 0xa4, 0x63, 0x2f, 0xf4, 0x86, 0xd1, - 0x07, 0xe5, 0xdb, 0xb6, 0x68, 0x0c, 0xa5, 0x7a, 0xdb, 0x0e, 0xb2, 0xab, 0x46, 0x7f, 0xd6, 0x5e, - 0x37, 0x9e, 0xb5, 0x3d, 0xf9, 0xfe, 0xc3, 0x06, 0xbf, 0xdd, 0xe9, 0x45, 0x8b, 0xca, 0x7a, 0x2c, - 0x91, 0xde, 0x7c, 0xa7, 0x57, 0xed, 0x61, 0xd7, 0x21, 0x8e, 0xc8, 0xa7, 0x61, 0xac, 0x66, 0x72, - 0x2d, 0x62, 0x25, 0x3e, 0x9e, 0x47, 0x37, 0xc1, 0xe4, 0xc4, 0x7c, 0x4a, 0x02, 0x80, 0x93, 0x84, - 0x51, 0x99, 0x87, 0x96, 0x59, 0xf6, 0xd6, 0x7d, 0xe1, 0x41, 0x65, 0xe7, 0xce, 0xe5, 0x4e, 0x18, - 0x91, 0x06, 0xc5, 0x8c, 0x99, 0x84, 0x55, 0x51, 0x17, 0x2b, 0x2a, 0xe8, 0x75, 0xe8, 0x63, 0x5e, - 0x8f, 0xe1, 0xd4, 0x40, 0xbe, 0x5a, 0xc3, 0x8c, 0x84, 0x1a, 0x6f, 0x48, 0xf6, 0x37, 0xc4, 0x82, - 0x02, 0xba, 0x2c, 0x7d, 0x8a, 0xc3, 0x65, 0xef, 0x7a, 0x48, 0x98, 0x4f, 0xf1, 0xe0, 0xdc, 0xa3, - 0xb1, 0xbb, 0x30, 0x2f, 0xcf, 0xcc, 0x3f, 0x6b, 0xd4, 0xa4, 0x6c, 0x9f, 0xf8, 0x2f, 0xd3, 0xda, - 0x8a, 0xb8, 0x6d, 0x99, 0xdd, 0x33, 0x53, 0xdf, 0xc6, 0xc3, 0x79, 0xc3, 0x24, 0x81, 0x93, 0x34, - 0x29, 0x0b, 0xcd, 0x77, 0xbd, 0xf0, 0xc1, 0xea, 0x74, 0x76, 0x70, 0xc9, 0x01, 0xbb, 0x8d, 0x78, - 0x09, 0x16, 0xf5, 0x91, 0x0b, 0x63, 0x81, 0xc1, 0x5e, 0xc8, 0x70, 0x6b, 0x67, 0xbb, 0x63, 0x62, - 0xb4, 0x40, 0xfe, 0x26, 0x19, 0x9c, 0xa4, 0x8b, 0x5e, 0xd7, 0x18, 0xa5, 0x91, 0xf6, 0x2f, 0xff, - 0x4e, 0xac, 0xd1, 0xf4, 0x16, 0x8c, 0x18, 0x87, 0xcd, 0x03, 0x55, 0x41, 0x7a, 0x30, 0x9e, 0x3c, - 0x59, 0x1e, 0xa8, 0xe6, 0xf1, 0xcf, 0x7a, 0x60, 0xd4, 0xdc, 0x09, 0xe8, 0x3c, 0x0c, 0x0a, 0x22, - 0x2a, 0xa3, 0x95, 0xda, 0xdc, 0x2b, 0x12, 0x80, 0x63, 0x1c, 0x96, 0xc8, 0x8c, 0x55, 0xd7, 0x7c, - 0x05, 0xe2, 0x44, 0x66, 0x0a, 0x82, 0x35, 0x2c, 0xfa, 0x80, 0xbd, 0xe5, 0xfb, 0x91, 0xba, 0x47, - 0xd5, 0x76, 0x99, 0x63, 0xa5, 0x58, 0x40, 0xe9, 0xfd, 0xb9, 0x45, 0x02, 0x8f, 0xd4, 0xcd, 0x94, - 0x0e, 0xea, 0xfe, 0xbc, 0xa2, 0x03, 0xb1, 0x89, 0x4b, 0xb9, 0x00, 0x3f, 0x64, 0xfb, 0x4f, 0x3c, - 0x93, 0x63, 0xdf, 0x8b, 0x0a, 0x8f, 0x22, 0x21, 0xe1, 0xe8, 0x63, 0x70, 0x5c, 0x85, 0x4f, 0x14, - 0xab, 0x4b, 0xb6, 0xd8, 0x67, 0x48, 0xb5, 0x8e, 0xcf, 0x67, 0xa3, 0xe1, 0xbc, 0xfa, 0xe8, 0x55, - 0x18, 0x15, 0x4f, 0x29, 0x49, 0xb1, 0xdf, 0x34, 0x24, 0xbc, 0x62, 0x40, 0x71, 0x02, 0x5b, 0x26, - 0xa5, 0x60, 0x6f, 0x0c, 0x49, 0x61, 0x20, 0x9d, 0x94, 0x42, 0x87, 0xe3, 0x54, 0x0d, 0x34, 0x0b, - 0x63, 0x9c, 0x75, 0x74, 0xbd, 0x0d, 0x3e, 0x27, 0xc2, 0xb3, 0x53, 0x6d, 0xaa, 0x6b, 0x26, 0x18, - 0x27, 0xf1, 0xd1, 0x45, 0x18, 0x76, 0x82, 0xea, 0xa6, 0x1b, 0x91, 0x2a, 0xdd, 0x19, 0xcc, 0x96, - 0x4f, 0xb3, 0xc4, 0x9c, 0xd5, 0x60, 0xd8, 0xc0, 0xb4, 0xef, 0xc2, 0x64, 0x46, 0x78, 0x19, 0xba, - 0x70, 0x9c, 0xa6, 0x2b, 0xbf, 0x29, 0xe1, 0xee, 0x30, 0x5b, 0x5e, 0x96, 0x5f, 0xa3, 0x61, 0xd1, - 0xd5, 0xc9, 0xc2, 0xd0, 0x68, 0xc9, 0xb7, 0xd5, 0xea, 0x5c, 0x92, 0x00, 0x1c, 0xe3, 0xd8, 0x7f, - 0x53, 0x80, 0xb1, 0x0c, 0x05, 0x1d, 0x4b, 0x00, 0x9d, 0x78, 0x69, 0xc5, 0xf9, 0x9e, 0xcd, 0x1c, - 0x27, 0x85, 0x03, 0xe4, 0x38, 0x29, 0x76, 0xca, 0x71, 0xd2, 0xf3, 0x4e, 0x72, 0x9c, 0x98, 0x23, - 0xd6, 0xdb, 0xd5, 0x88, 0x65, 0xe4, 0x45, 0xe9, 0x3b, 0x60, 0x5e, 0x14, 0x63, 0xd0, 0xfb, 0xbb, - 0x18, 0xf4, 0x1f, 0x2d, 0xc0, 0x78, 0x52, 0xb7, 0x77, 0x08, 0xf2, 0xf1, 0xd7, 0x0d, 0xf9, 0xf8, - 0xb9, 0x6e, 0x3c, 0xf1, 0x73, 0x65, 0xe5, 0x38, 0x21, 0x2b, 0x7f, 0xb2, 0x2b, 0x6a, 0xed, 0xe5, - 0xe6, 0xff, 0xa8, 0x00, 0x47, 0x33, 0x55, 0x9e, 0x87, 0x30, 0x36, 0xd7, 0x8c, 0xb1, 0x79, 0xa6, - 0xeb, 0x28, 0x05, 0xb9, 0x03, 0x74, 0x33, 0x31, 0x40, 0xe7, 0xbb, 0x27, 0xd9, 0x7e, 0x94, 0xbe, - 0x51, 0x84, 0xd3, 0x99, 0xf5, 0x62, 0xf1, 0xf2, 0x92, 0x21, 0x5e, 0x7e, 0x36, 0x21, 0x5e, 0xb6, - 0xdb, 0xd7, 0xbe, 0x3f, 0xf2, 0x66, 0xe1, 0xad, 0xcf, 0x62, 0x8e, 0xdc, 0xa3, 0xac, 0xd9, 0xf0, - 0xd6, 0x57, 0x84, 0xb0, 0x49, 0xf7, 0xbd, 0x24, 0x63, 0xfe, 0x7d, 0x0b, 0x4e, 0x64, 0xce, 0xcd, - 0x21, 0x48, 0xfa, 0x56, 0x4d, 0x49, 0xdf, 0x13, 0x5d, 0xaf, 0xd6, 0x1c, 0xd1, 0xdf, 0xe7, 0xfb, - 0x72, 0xbe, 0x85, 0x09, 0x20, 0xae, 0xc1, 0x90, 0x53, 0xad, 0x92, 0x30, 0x5c, 0xf1, 0x6b, 0x2a, - 0x1d, 0xc2, 0x33, 0xec, 0x79, 0x18, 0x17, 0xef, 0xef, 0x96, 0xa6, 0x93, 0x24, 0x62, 0x30, 0xd6, - 0x29, 0xa0, 0x4f, 0xc0, 0x40, 0x28, 0x33, 0x59, 0xf6, 0xdc, 0x7b, 0x26, 0x4b, 0xc6, 0xe4, 0x2a, - 0x01, 0x8b, 0x22, 0x89, 0xbe, 0x53, 0x8f, 0xfe, 0xd4, 0x46, 0xb4, 0xc8, 0x3b, 0x79, 0x0f, 0x31, - 0xa0, 0x9e, 0x05, 0xd8, 0x56, 0x2f, 0x99, 0xa4, 0xf0, 0x44, 0x7b, 0xe3, 0x68, 0x58, 0xe8, 0x35, - 0x18, 0x0f, 0x79, 0xe0, 0xd3, 0xd8, 0x48, 0x85, 0xaf, 0x45, 0x16, 0x3b, 0xae, 0x92, 0x80, 0xe1, - 0x14, 0x36, 0x5a, 0x92, 0xad, 0x32, 0x73, 0x24, 0xbe, 0x3c, 0xcf, 0xc6, 0x2d, 0x0a, 0x93, 0xa4, - 0x23, 0xc9, 0x49, 0x60, 0xc3, 0xaf, 0xd5, 0x44, 0x9f, 0x00, 0xa0, 0x8b, 0x48, 0x08, 0x51, 0xfa, - 0xf3, 0x8f, 0x50, 0x7a, 0xb6, 0xd4, 0x32, 0x3d, 0x19, 0x98, 0x9b, 0xfd, 0x82, 0x22, 0x82, 0x35, - 0x82, 0xc8, 0x81, 0x91, 0xf8, 0x5f, 0x9c, 0xa3, 0xfd, 0x5c, 0x6e, 0x0b, 0x49, 0xe2, 0x4c, 0xc1, - 0xb0, 0xa0, 0x93, 0xc0, 0x26, 0x45, 0xf4, 0x71, 0x38, 0xb1, 0x9d, 0x6b, 0xf9, 0xc3, 0x39, 0x41, - 0x96, 0x74, 0x3d, 0xdf, 0xde, 0x27, 0xbf, 0xbe, 0xfd, 0xef, 0x00, 0x1e, 0x6a, 0x73, 0xd2, 0xa3, - 0x59, 0x53, 0x6b, 0xff, 0x54, 0x52, 0xb2, 0x31, 0x9d, 0x59, 0xd9, 0x10, 0x75, 0x24, 0x36, 0x54, - 0xe1, 0x1d, 0x6f, 0xa8, 0x1f, 0xb2, 0x34, 0x99, 0x13, 0xb7, 0xe9, 0xfe, 0xc8, 0x01, 0x6f, 0xb0, - 0xfb, 0x28, 0x84, 0x5a, 0xcf, 0x90, 0xe4, 0x3c, 0xdb, 0x75, 0x77, 0xba, 0x17, 0xed, 0x7c, 0x35, - 0x3b, 0xe0, 0x3b, 0x17, 0xf2, 0x5c, 0x3a, 0xe8, 0xf7, 0x1f, 0x56, 0xf0, 0xf7, 0xaf, 0x5b, 0x70, - 0x22, 0x55, 0xcc, 0xfb, 0x40, 0x42, 0x11, 0xed, 0x6e, 0xf5, 0x1d, 0x77, 0x5e, 0x12, 0xe4, 0xdf, - 0x70, 0x59, 0x7c, 0xc3, 0x89, 0x5c, 0xbc, 0x64, 0xd7, 0xbf, 0xf0, 0xa7, 0xa5, 0x49, 0xd6, 0x80, - 0x89, 0x88, 0xf3, 0xbb, 0x8e, 0x9a, 0x70, 0xa6, 0xda, 0x0a, 0x82, 0x78, 0xb1, 0x66, 0x6c, 0x4e, - 0xfe, 0xd6, 0x7b, 0x74, 0x6f, 0xb7, 0x74, 0x66, 0xbe, 0x03, 0x2e, 0xee, 0x48, 0x0d, 0x79, 0x80, - 0x1a, 0x29, 0xfb, 0x3a, 0x76, 0x00, 0xe4, 0xc8, 0x61, 0xd2, 0xd6, 0x78, 0xdc, 0x52, 0x36, 0xc3, - 0x4a, 0x2f, 0x83, 0xf2, 0xe1, 0x4a, 0x4f, 0xbe, 0x35, 0x71, 0xe9, 0xa7, 0xaf, 0xc2, 0xe9, 0xf6, - 0x8b, 0xe9, 0x40, 0xa1, 0x1c, 0xfe, 0xc8, 0x82, 0x53, 0x6d, 0xe3, 0x85, 0x7d, 0x1b, 0x3e, 0x16, - 0xec, 0xcf, 0x5a, 0xf0, 0x70, 0x66, 0x8d, 0xa4, 0x13, 0x5e, 0x95, 0x16, 0x6a, 0xe6, 0xa8, 0x71, - 0xe4, 0x1c, 0x09, 0xc0, 0x31, 0x8e, 0x61, 0xb1, 0x59, 0xe8, 0x68, 0xb1, 0xf9, 0xdb, 0x16, 0xa4, - 0xae, 0xfa, 0x43, 0xe0, 0x3c, 0x97, 0x4d, 0xce, 0xf3, 0xd1, 0x6e, 0x46, 0x33, 0x87, 0xe9, 0xfc, - 0xab, 0x31, 0x38, 0x96, 0xe3, 0x89, 0xbd, 0x0d, 0x13, 0x1b, 0x55, 0x62, 0x86, 0xde, 0x68, 0x17, - 0x92, 0xae, 0x6d, 0x9c, 0x8e, 0xb9, 0xa3, 0x7b, 0xbb, 0xa5, 0x89, 0x14, 0x0a, 0x4e, 0x37, 0x81, - 0x3e, 0x6b, 0xc1, 0x11, 0xe7, 0x76, 0xb8, 0x48, 0x5f, 0x10, 0x6e, 0x75, 0xae, 0xee, 0x57, 0xb7, - 0x28, 0x63, 0x26, 0xb7, 0xd5, 0xf3, 0x99, 0xc2, 0xe8, 0x9b, 0x95, 0x14, 0xbe, 0xd1, 0xfc, 0xd4, - 0xde, 0x6e, 0xe9, 0x48, 0x16, 0x16, 0xce, 0x6c, 0x0b, 0x61, 0x91, 0xf1, 0xcb, 0x89, 0x36, 0xdb, - 0x05, 0x87, 0xc9, 0x72, 0x99, 0xe7, 0x2c, 0xb1, 0x84, 0x60, 0x45, 0x07, 0x7d, 0x0a, 0x06, 0x37, - 0x64, 0x1c, 0x88, 0x0c, 0x96, 0x3b, 0x1e, 0xc8, 0xf6, 0xd1, 0x31, 0xb8, 0x09, 0x8c, 0x42, 0xc2, - 0x31, 0x51, 0xf4, 0x2a, 0x14, 0xbd, 0xf5, 0x50, 0x84, 0xa8, 0xcb, 0xb6, 0xc4, 0x35, 0x6d, 0x9d, - 0x79, 0x08, 0xa6, 0xd5, 0xa5, 0x0a, 0xa6, 0x15, 0xd1, 0x65, 0x28, 0x06, 0xb7, 0x6a, 0x42, 0x93, - 0x92, 0xb9, 0x49, 0xf1, 0xdc, 0x42, 0x4e, 0xaf, 0x18, 0x25, 0x3c, 0xb7, 0x80, 0x29, 0x09, 0x54, - 0x86, 0x5e, 0xe6, 0xbe, 0x2c, 0x58, 0xdb, 0xcc, 0xa7, 0x7c, 0x9b, 0x30, 0x00, 0xdc, 0x23, 0x91, - 0x21, 0x60, 0x4e, 0x08, 0xad, 0x41, 0x5f, 0xd5, 0xf5, 0x6a, 0x24, 0x10, 0xbc, 0xec, 0x07, 0x33, - 0x75, 0x26, 0x0c, 0x23, 0x87, 0x26, 0x57, 0x21, 0x30, 0x0c, 0x2c, 0x68, 0x31, 0xaa, 0xa4, 0xb9, - 0xb9, 0x2e, 0x6f, 0xac, 0x6c, 0xaa, 0xa4, 0xb9, 0xb9, 0x54, 0x69, 0x4b, 0x95, 0x61, 0x60, 0x41, - 0x0b, 0xbd, 0x04, 0x85, 0xf5, 0xaa, 0x70, 0x4d, 0xce, 0x54, 0x9e, 0x98, 0x51, 0xb4, 0xe6, 0xfa, - 0xf6, 0x76, 0x4b, 0x85, 0xa5, 0x79, 0x5c, 0x58, 0xaf, 0xa2, 0x55, 0xe8, 0x5f, 0xe7, 0x71, 0x77, - 0x84, 0x7e, 0xe4, 0xf1, 0xec, 0x90, 0x40, 0xa9, 0xd0, 0x3c, 0xdc, 0xbb, 0x54, 0x00, 0xb0, 0x24, - 0xc2, 0x12, 0x50, 0xa9, 0xf8, 0x41, 0x22, 0x7c, 0xe9, 0xcc, 0xc1, 0x62, 0x3e, 0xf1, 0xa7, 0x46, - 0x1c, 0x85, 0x08, 0x6b, 0x14, 0xe9, 0xaa, 0x76, 0xee, 0xb6, 0x02, 0x96, 0xdb, 0x42, 0xa8, 0x46, - 0x32, 0x57, 0xf5, 0xac, 0x44, 0x6a, 0xb7, 0xaa, 0x15, 0x12, 0x8e, 0x89, 0xa2, 0x2d, 0x18, 0xd9, - 0x0e, 0x9b, 0x9b, 0x44, 0x6e, 0x69, 0x16, 0xf6, 0x2e, 0x87, 0x9b, 0xbd, 0x21, 0x10, 0xdd, 0x20, - 0x6a, 0x39, 0xf5, 0xd4, 0x29, 0xc4, 0x9e, 0x35, 0x37, 0x74, 0x62, 0xd8, 0xa4, 0x4d, 0x87, 0xff, - 0xed, 0x96, 0x7f, 0x6b, 0x27, 0x22, 0x22, 0xea, 0x68, 0xe6, 0xf0, 0xbf, 0xc1, 0x51, 0xd2, 0xc3, - 0x2f, 0x00, 0x58, 0x12, 0x41, 0x37, 0xc4, 0xf0, 0xb0, 0xd3, 0x73, 0x3c, 0x3f, 0xa4, 0xf9, 0xac, - 0x44, 0xca, 0x19, 0x14, 0x76, 0x5a, 0xc6, 0xa4, 0xd8, 0x29, 0xd9, 0xdc, 0xf4, 0x23, 0xdf, 0x4b, - 0x9c, 0xd0, 0x13, 0xf9, 0xa7, 0x64, 0x39, 0x03, 0x3f, 0x7d, 0x4a, 0x66, 0x61, 0xe1, 0xcc, 0xb6, - 0x50, 0x0d, 0x46, 0x9b, 0x7e, 0x10, 0xdd, 0xf6, 0x03, 0xb9, 0xbe, 0x50, 0x1b, 0x41, 0xa9, 0x81, - 0x29, 0x5a, 0x64, 0x86, 0x39, 0x26, 0x04, 0x27, 0x68, 0xa2, 0x8f, 0x42, 0x7f, 0x58, 0x75, 0xea, - 0x64, 0xf9, 0xda, 0xd4, 0x64, 0xfe, 0xf5, 0x53, 0xe1, 0x28, 0x39, 0xab, 0x8b, 0x87, 0x4d, 0xe2, - 0x28, 0x58, 0x92, 0x43, 0x4b, 0xd0, 0xcb, 0x12, 0x3b, 0xb3, 0x10, 0xb9, 0x39, 0x91, 0xd9, 0x53, - 0x6e, 0x35, 0xfc, 0x6c, 0x62, 0xc5, 0x98, 0x57, 0xa7, 0x7b, 0x40, 0x48, 0x0a, 0xfc, 0x70, 0xea, - 0x68, 0xfe, 0x1e, 0x10, 0x02, 0x86, 0x6b, 0x95, 0x76, 0x7b, 0x40, 0x21, 0xe1, 0x98, 0x28, 0x3d, - 0x99, 0xe9, 0x69, 0x7a, 0xac, 0x8d, 0xc9, 0x64, 0xee, 0x59, 0xca, 0x4e, 0x66, 0x7a, 0x92, 0x52, - 0x12, 0xf6, 0x6f, 0x0e, 0xa4, 0x79, 0x16, 0x26, 0x61, 0xfa, 0xff, 0xad, 0x94, 0xcd, 0xc4, 0x87, - 0xba, 0x15, 0x78, 0xdf, 0xc7, 0x87, 0xeb, 0x67, 0x2d, 0x38, 0xd6, 0xcc, 0xfc, 0x10, 0xc1, 0x00, - 0x74, 0x27, 0x37, 0xe7, 0x9f, 0xae, 0xc2, 0x29, 0x67, 0xc3, 0x71, 0x4e, 0x4b, 0x49, 0xe1, 0x40, - 0xf1, 0x1d, 0x0b, 0x07, 0x56, 0x60, 0xa0, 0xca, 0x5f, 0x72, 0x32, 0x0d, 0x40, 0x57, 0xc1, 0x40, - 0x19, 0x2b, 0x21, 0x9e, 0x80, 0xeb, 0x58, 0x91, 0x40, 0x3f, 0x6c, 0xc1, 0xa9, 0x64, 0xd7, 0x31, - 0x61, 0x60, 0x61, 0x30, 0xc9, 0xc5, 0x5a, 0x4b, 0xe2, 0xfb, 0x53, 0xfc, 0xbf, 0x81, 0xbc, 0xdf, - 0x09, 0x01, 0xb7, 0x6f, 0x0c, 0x2d, 0x64, 0xc8, 0xd5, 0xfa, 0x4c, 0x8d, 0x62, 0x17, 0xb2, 0xb5, - 0xe7, 0x61, 0xb8, 0xe1, 0xb7, 0xbc, 0x48, 0xd8, 0x3d, 0x0a, 0xe3, 0x29, 0x66, 0x34, 0xb4, 0xa2, - 0x95, 0x63, 0x03, 0x2b, 0x21, 0x91, 0x1b, 0xb8, 0x67, 0x89, 0xdc, 0x5b, 0x30, 0xec, 0x69, 0x2e, - 0x01, 0xed, 0x5e, 0xb0, 0x42, 0xba, 0xa8, 0x61, 0xf3, 0x5e, 0xea, 0x25, 0xd8, 0xa0, 0xd6, 0x5e, - 0x5a, 0x06, 0xef, 0x4c, 0x5a, 0x76, 0xa8, 0x4f, 0x62, 0xfb, 0x17, 0x0a, 0x19, 0x2f, 0x06, 0x2e, - 0x95, 0x7b, 0xc5, 0x94, 0xca, 0x9d, 0x4d, 0x4a, 0xe5, 0x52, 0xaa, 0x2a, 0x43, 0x20, 0xd7, 0x7d, - 0x46, 0xc9, 0xae, 0x03, 0x3c, 0x7f, 0xaf, 0x05, 0xc7, 0x99, 0xee, 0x83, 0x36, 0xf0, 0x8e, 0xf5, - 0x1d, 0xcc, 0x24, 0xf5, 0x6a, 0x36, 0x39, 0x9c, 0xd7, 0x8e, 0x5d, 0x87, 0x33, 0x9d, 0xee, 0x5d, - 0x66, 0xe1, 0x5b, 0x53, 0xc6, 0x11, 0xb1, 0x85, 0x6f, 0x6d, 0x79, 0x01, 0x33, 0x48, 0xb7, 0xe1, - 0x0b, 0xed, 0xff, 0x6e, 0x41, 0xb1, 0xec, 0xd7, 0x0e, 0xe1, 0x45, 0xff, 0x11, 0xe3, 0x45, 0xff, - 0x50, 0xf6, 0x8d, 0x5f, 0xcb, 0x55, 0xf6, 0x2d, 0x26, 0x94, 0x7d, 0xa7, 0xf2, 0x08, 0xb4, 0x57, - 0xed, 0xfd, 0x74, 0x11, 0x86, 0xca, 0x7e, 0x4d, 0xed, 0xb3, 0x7f, 0x73, 0x2f, 0x8e, 0x3c, 0xb9, - 0xd9, 0xa7, 0x34, 0xca, 0xcc, 0xa2, 0x57, 0xc6, 0x9d, 0xf8, 0x36, 0xf3, 0xe7, 0xb9, 0x49, 0xdc, - 0x8d, 0xcd, 0x88, 0xd4, 0x92, 0x9f, 0x73, 0x78, 0xfe, 0x3c, 0xdf, 0x2c, 0xc2, 0x58, 0xa2, 0x75, - 0x54, 0x87, 0x91, 0xba, 0xae, 0x4a, 0x12, 0xeb, 0xf4, 0x9e, 0xb4, 0x50, 0xc2, 0x1f, 0x42, 0x2b, - 0xc2, 0x26, 0x71, 0x34, 0x03, 0xe0, 0xe9, 0x56, 0xe1, 0x2a, 0x50, 0xb1, 0x66, 0x11, 0xae, 0x61, - 0xa0, 0x17, 0x60, 0x28, 0xf2, 0x9b, 0x7e, 0xdd, 0xdf, 0xd8, 0xb9, 0x42, 0x64, 0x64, 0x4b, 0x65, - 0x34, 0xbc, 0x16, 0x83, 0xb0, 0x8e, 0x87, 0xee, 0xc0, 0x84, 0x22, 0x52, 0xb9, 0x0f, 0xea, 0x35, - 0x26, 0x36, 0x59, 0x4d, 0x52, 0xc4, 0xe9, 0x46, 0xd0, 0x4b, 0x30, 0xca, 0xac, 0x97, 0x59, 0xfd, - 0x2b, 0x64, 0x47, 0x46, 0x3c, 0x66, 0x1c, 0xf6, 0x8a, 0x01, 0xc1, 0x09, 0x4c, 0x34, 0x0f, 0x13, - 0x0d, 0x37, 0x4c, 0x54, 0xef, 0x63, 0xd5, 0x59, 0x07, 0x56, 0x92, 0x40, 0x9c, 0xc6, 0xb7, 0x7f, - 0x4e, 0xcc, 0xb1, 0x17, 0xb9, 0xef, 0x6f, 0xc7, 0x77, 0xf7, 0x76, 0xfc, 0x86, 0x05, 0xe3, 0xb4, - 0x75, 0x66, 0x92, 0x29, 0x19, 0x29, 0x95, 0x13, 0xc3, 0x6a, 0x93, 0x13, 0xe3, 0x2c, 0x3d, 0xb6, - 0x6b, 0x7e, 0x2b, 0x12, 0xd2, 0x51, 0xed, 0x5c, 0xa6, 0xa5, 0x58, 0x40, 0x05, 0x1e, 0x09, 0x02, - 0xe1, 0xf7, 0xae, 0xe3, 0x91, 0x20, 0xc0, 0x02, 0x2a, 0x53, 0x66, 0xf4, 0x64, 0xa7, 0xcc, 0xe0, - 0x91, 0xcf, 0x85, 0x15, 0x9c, 0x60, 0x69, 0xb5, 0xc8, 0xe7, 0xd2, 0x3c, 0x2e, 0xc6, 0xb1, 0xbf, - 0x5a, 0x84, 0xe1, 0xb2, 0x5f, 0x8b, 0x0d, 0x3b, 0x9e, 0x37, 0x0c, 0x3b, 0xce, 0x24, 0x0c, 0x3b, - 0xc6, 0x75, 0xdc, 0xf7, 0xcd, 0x38, 0xbe, 0x55, 0x66, 0x1c, 0xbf, 0x65, 0xb1, 0x59, 0x5b, 0x58, - 0xad, 0x70, 0x0b, 0x5f, 0x74, 0x01, 0x86, 0xd8, 0x09, 0xc7, 0x02, 0x2d, 0x48, 0x6b, 0x07, 0x96, - 0xc2, 0x72, 0x35, 0x2e, 0xc6, 0x3a, 0x0e, 0x3a, 0x07, 0x03, 0x21, 0x71, 0x82, 0xea, 0xa6, 0x3a, - 0xde, 0x85, 0x69, 0x02, 0x2f, 0xc3, 0x0a, 0x8a, 0xde, 0x88, 0x83, 0x6e, 0x17, 0xf3, 0xcd, 0x85, - 0xf5, 0xfe, 0xf0, 0x2d, 0x92, 0x1f, 0x69, 0xdb, 0xbe, 0x09, 0x28, 0x8d, 0xdf, 0x85, 0xff, 0x55, - 0xc9, 0x0c, 0x0b, 0x3b, 0x98, 0x0a, 0x09, 0xfb, 0xb7, 0x16, 0x8c, 0x96, 0xfd, 0x1a, 0xdd, 0xba, - 0xef, 0xa5, 0x7d, 0xaa, 0x67, 0x1c, 0xe8, 0x6b, 0x93, 0x71, 0xe0, 0x11, 0xe8, 0x2d, 0xfb, 0xb5, - 0x0e, 0xa1, 0x6b, 0xff, 0xb1, 0x05, 0xfd, 0x65, 0xbf, 0x76, 0x08, 0x8a, 0x97, 0x57, 0x4c, 0xc5, - 0xcb, 0xf1, 0x9c, 0x75, 0x93, 0xa3, 0x6b, 0xf9, 0xdd, 0x1e, 0x18, 0xa1, 0xfd, 0xf4, 0x37, 0xe4, - 0x54, 0x1a, 0xc3, 0x66, 0x75, 0x31, 0x6c, 0xf4, 0x19, 0xe0, 0xd7, 0xeb, 0xfe, 0xed, 0xe4, 0xb4, - 0x2e, 0xb1, 0x52, 0x2c, 0xa0, 0xe8, 0x69, 0x18, 0x68, 0x06, 0x64, 0xdb, 0xf5, 0x05, 0x7f, 0xad, - 0xa9, 0xb1, 0xca, 0xa2, 0x1c, 0x2b, 0x0c, 0xfa, 0xf0, 0x0e, 0x5d, 0x8f, 0xf2, 0x12, 0x55, 0xdf, - 0xab, 0x71, 0xdd, 0x44, 0x51, 0xa4, 0xc5, 0xd2, 0xca, 0xb1, 0x81, 0x85, 0x6e, 0xc2, 0x20, 0xfb, - 0xcf, 0x8e, 0x9d, 0xde, 0x03, 0x1f, 0x3b, 0x22, 0x51, 0xb0, 0x20, 0x80, 0x63, 0x5a, 0xe8, 0x59, - 0x80, 0x48, 0xa6, 0x96, 0x09, 0x45, 0x08, 0x53, 0xf5, 0x16, 0x51, 0x49, 0x67, 0x42, 0xac, 0x61, - 0xa1, 0xa7, 0x60, 0x30, 0x72, 0xdc, 0xfa, 0x55, 0xd7, 0x63, 0xfa, 0x7b, 0xda, 0x7f, 0x91, 0xaf, - 0x57, 0x14, 0xe2, 0x18, 0x4e, 0x79, 0x41, 0x16, 0x13, 0x6a, 0x6e, 0x27, 0x12, 0xa9, 0xe9, 0x8a, - 0x9c, 0x17, 0xbc, 0xaa, 0x4a, 0xb1, 0x86, 0x81, 0x36, 0xe1, 0xa4, 0xeb, 0xb1, 0x14, 0x52, 0xa4, - 0xb2, 0xe5, 0x36, 0xd7, 0xae, 0x56, 0x6e, 0x90, 0xc0, 0x5d, 0xdf, 0x99, 0x73, 0xaa, 0x5b, 0xc4, - 0x93, 0x09, 0xf1, 0x1f, 0x15, 0x5d, 0x3c, 0xb9, 0xdc, 0x06, 0x17, 0xb7, 0xa5, 0x84, 0x6c, 0xba, - 0x1d, 0x03, 0xe2, 0x34, 0x84, 0x4c, 0x80, 0xa7, 0x9f, 0x61, 0x25, 0x58, 0x40, 0xec, 0xe7, 0xd8, - 0x9e, 0xb8, 0x56, 0x41, 0x4f, 0x1a, 0xc7, 0xcb, 0x31, 0xfd, 0x78, 0xd9, 0xdf, 0x2d, 0xf5, 0x5d, - 0xab, 0x68, 0xf1, 0x81, 0x2e, 0xc2, 0xd1, 0xb2, 0x5f, 0x2b, 0xfb, 0x41, 0xb4, 0xe4, 0x07, 0xb7, - 0x9d, 0xa0, 0x26, 0x97, 0x60, 0x49, 0x46, 0x48, 0xa2, 0x67, 0x6c, 0x2f, 0x3f, 0x81, 0x8c, 0xe8, - 0x47, 0xcf, 0x31, 0xae, 0xee, 0x80, 0x0e, 0xa9, 0x55, 0xc6, 0x5f, 0xa8, 0x44, 0x6d, 0x97, 0x9c, - 0x88, 0xa0, 0x6b, 0x30, 0x52, 0xd5, 0xaf, 0x5a, 0x51, 0xfd, 0x09, 0x79, 0xd9, 0x19, 0xf7, 0x70, - 0xe6, 0xdd, 0x6c, 0xd6, 0xb7, 0xbf, 0x6e, 0x89, 0x56, 0xb8, 0xb4, 0x82, 0xdb, 0xbd, 0x76, 0x3e, - 0x73, 0xe7, 0x61, 0x22, 0xd0, 0xab, 0x68, 0xf6, 0x63, 0x47, 0x79, 0xe6, 0x9b, 0x04, 0x10, 0xa7, - 0xf1, 0xd1, 0xc7, 0xe1, 0x84, 0x51, 0x28, 0x55, 0xe9, 0x5a, 0xfe, 0x69, 0x26, 0xcf, 0xc1, 0x79, - 0x48, 0x38, 0xbf, 0xbe, 0xfd, 0xdd, 0x70, 0x2c, 0xf9, 0x5d, 0x42, 0xc2, 0x72, 0x8f, 0x5f, 0x57, - 0x38, 0xd8, 0xd7, 0xd9, 0x2f, 0xc0, 0x04, 0x7d, 0x7a, 0x2b, 0x36, 0x92, 0xcd, 0x5f, 0xe7, 0x20, - 0x54, 0xbf, 0x34, 0xc0, 0xae, 0xc1, 0x44, 0xf6, 0x35, 0xf4, 0x49, 0x18, 0x0d, 0x09, 0x8b, 0xbc, - 0x26, 0x25, 0x7b, 0x6d, 0xbc, 0xc9, 0x2b, 0x8b, 0x3a, 0x26, 0x7f, 0xbd, 0x98, 0x65, 0x38, 0x41, - 0x0d, 0x35, 0x60, 0xf4, 0xb6, 0xeb, 0xd5, 0xfc, 0xdb, 0xa1, 0xa4, 0x3f, 0x90, 0xaf, 0x26, 0xb8, - 0xc9, 0x31, 0x13, 0x7d, 0x34, 0x9a, 0xbb, 0x69, 0x10, 0xc3, 0x09, 0xe2, 0xf4, 0xa8, 0x09, 0x5a, - 0xde, 0x6c, 0x78, 0x3d, 0x24, 0x81, 0x88, 0x0b, 0xc7, 0x8e, 0x1a, 0x2c, 0x0b, 0x71, 0x0c, 0xa7, - 0x47, 0x0d, 0xfb, 0xc3, 0xdc, 0xd1, 0xd9, 0x59, 0x26, 0x8e, 0x1a, 0xac, 0x4a, 0xb1, 0x86, 0x41, - 0x8f, 0x62, 0xf6, 0x6f, 0xd5, 0xf7, 0xb0, 0xef, 0x47, 0xf2, 0xf0, 0x66, 0xa9, 0x2a, 0xb5, 0x72, - 0x6c, 0x60, 0xe5, 0x44, 0xa1, 0xeb, 0x39, 0x68, 0x14, 0x3a, 0x14, 0xb5, 0xf1, 0xc0, 0xe7, 0xd1, - 0x90, 0x2f, 0xb6, 0xf3, 0xc0, 0xdf, 0xbf, 0x27, 0xef, 0x7c, 0xca, 0x0b, 0xac, 0x8b, 0x01, 0xea, - 0xe5, 0x61, 0xf6, 0x98, 0x22, 0xb3, 0xc2, 0x47, 0x47, 0xc2, 0xd0, 0x22, 0xf4, 0x87, 0x3b, 0x61, - 0x35, 0xaa, 0x87, 0xed, 0xd2, 0x91, 0x56, 0x18, 0x8a, 0x96, 0x0d, 0x9b, 0x57, 0xc1, 0xb2, 0x2e, - 0xaa, 0xc2, 0xa4, 0xa0, 0x38, 0xbf, 0xe9, 0x78, 0x2a, 0x49, 0x22, 0xb7, 0x58, 0xbc, 0xb0, 0xb7, - 0x5b, 0x9a, 0x14, 0x2d, 0xeb, 0xe0, 0xfd, 0xdd, 0x12, 0xdd, 0x92, 0x19, 0x10, 0x9c, 0x45, 0x8d, - 0x2f, 0xf9, 0x6a, 0xd5, 0x6f, 0x34, 0xcb, 0x81, 0xbf, 0xee, 0xd6, 0x49, 0x3b, 0x65, 0x70, 0xc5, - 0xc0, 0x14, 0x4b, 0xde, 0x28, 0xc3, 0x09, 0x6a, 0xe8, 0x16, 0x8c, 0x39, 0xcd, 0xe6, 0x6c, 0xd0, - 0xf0, 0x03, 0xd9, 0xc0, 0x50, 0xbe, 0x56, 0x61, 0xd6, 0x44, 0xe5, 0x39, 0x12, 0x13, 0x85, 0x38, - 0x49, 0x90, 0x0e, 0x94, 0xd8, 0x68, 0xc6, 0x40, 0x8d, 0xc4, 0x03, 0x25, 0xf6, 0x65, 0xc6, 0x40, - 0x65, 0x40, 0x70, 0x16, 0x35, 0xfb, 0xbb, 0x18, 0xe3, 0x5f, 0x71, 0x37, 0x3c, 0xe6, 0x1c, 0x87, - 0x1a, 0x30, 0xd2, 0x64, 0xc7, 0xbe, 0xc8, 0x5f, 0x26, 0x8e, 0x8a, 0xe7, 0xbb, 0x14, 0x5e, 0xde, - 0x66, 0x19, 0x58, 0x0d, 0x23, 0xd6, 0xb2, 0x4e, 0x0e, 0x9b, 0xd4, 0xed, 0x7f, 0x7f, 0x82, 0xb1, - 0x8e, 0x15, 0x2e, 0x91, 0xec, 0x17, 0xae, 0x8a, 0x42, 0x06, 0x31, 0x9d, 0x2f, 0xfb, 0x8f, 0xd7, - 0x97, 0x70, 0x77, 0xc4, 0xb2, 0x2e, 0xfa, 0x04, 0x8c, 0xd2, 0x27, 0xbd, 0x62, 0xdf, 0xc2, 0xa9, - 0x23, 0xf9, 0x31, 0xb0, 0x14, 0x96, 0x9e, 0xdb, 0x50, 0xaf, 0x8c, 0x13, 0xc4, 0xd0, 0x1b, 0xcc, - 0xae, 0x53, 0x92, 0x2e, 0x74, 0x43, 0x5a, 0x37, 0xe1, 0x94, 0x64, 0x35, 0x22, 0xa8, 0x05, 0x93, - 0xe9, 0x0c, 0xce, 0xe1, 0x94, 0x9d, 0xff, 0x36, 0x4a, 0x27, 0x61, 0x8e, 0x93, 0xd0, 0xa5, 0x61, - 0x21, 0xce, 0xa2, 0x8f, 0xae, 0x26, 0xf3, 0xeb, 0x16, 0x0d, 0xad, 0x41, 0x2a, 0xc7, 0xee, 0x48, - 0xdb, 0xd4, 0xba, 0x1b, 0x70, 0x4a, 0x4b, 0x51, 0x7a, 0x29, 0x70, 0x98, 0x5d, 0x91, 0xcb, 0x6e, - 0x23, 0x8d, 0xa9, 0x7d, 0x78, 0x6f, 0xb7, 0x74, 0x6a, 0xad, 0x1d, 0x22, 0x6e, 0x4f, 0x07, 0x5d, - 0x83, 0xa3, 0x3c, 0x82, 0xcb, 0x02, 0x71, 0x6a, 0x75, 0xd7, 0x53, 0x5c, 0x33, 0x3f, 0xbb, 0x4e, - 0xec, 0xed, 0x96, 0x8e, 0xce, 0x66, 0x21, 0xe0, 0xec, 0x7a, 0xe8, 0x15, 0x18, 0xac, 0x79, 0xf2, - 0x94, 0xed, 0x33, 0xb2, 0xc0, 0x0e, 0x2e, 0xac, 0x56, 0xd4, 0xf7, 0xc7, 0x7f, 0x70, 0x5c, 0x01, - 0x6d, 0x70, 0xb5, 0x95, 0x92, 0x35, 0xf6, 0xa7, 0x02, 0x7b, 0x26, 0xc5, 0xf1, 0x46, 0x48, 0x04, - 0xae, 0xaf, 0x55, 0x2e, 0x77, 0x46, 0xb4, 0x04, 0x83, 0x30, 0x7a, 0x1d, 0x90, 0xc8, 0x36, 0x34, - 0x5b, 0x65, 0xc9, 0xf1, 0x34, 0x5b, 0x52, 0x25, 0x42, 0xa8, 0xa4, 0x30, 0x70, 0x46, 0x2d, 0x74, - 0x99, 0x1e, 0x8f, 0x7a, 0xa9, 0x38, 0x7e, 0x55, 0xae, 0xf1, 0x05, 0xd2, 0x0c, 0x08, 0x33, 0x7f, - 0x34, 0x29, 0xe2, 0x44, 0x3d, 0x54, 0x83, 0x93, 0x4e, 0x2b, 0xf2, 0x99, 0x46, 0xd0, 0x44, 0x5d, - 0xf3, 0xb7, 0x88, 0xc7, 0x94, 0xf1, 0x03, 0x2c, 0x60, 0xe8, 0xc9, 0xd9, 0x36, 0x78, 0xb8, 0x2d, - 0x15, 0xfa, 0x9c, 0xa2, 0x63, 0xa1, 0x29, 0xeb, 0x0c, 0xef, 0x6e, 0xae, 0xc1, 0x96, 0x18, 0xe8, - 0x05, 0x18, 0xda, 0xf4, 0xc3, 0x68, 0x95, 0x44, 0xb7, 0xfd, 0x60, 0x4b, 0xa4, 0x37, 0x88, 0x53, - 0xca, 0xc4, 0x20, 0xac, 0xe3, 0xa1, 0x27, 0xa0, 0x9f, 0x99, 0x8a, 0x2d, 0x2f, 0xb0, 0xbb, 0x76, - 0x20, 0x3e, 0x63, 0x2e, 0xf3, 0x62, 0x2c, 0xe1, 0x12, 0x75, 0xb9, 0x3c, 0xcf, 0x8e, 0xe3, 0x04, - 0xea, 0x72, 0x79, 0x1e, 0x4b, 0x38, 0x5d, 0xae, 0xe1, 0xa6, 0x13, 0x90, 0x72, 0xe0, 0x57, 0x49, - 0xa8, 0x25, 0x32, 0x7a, 0x88, 0x27, 0x6f, 0xa0, 0xcb, 0xb5, 0x92, 0x85, 0x80, 0xb3, 0xeb, 0x21, - 0x92, 0x4e, 0xcf, 0x3b, 0x9a, 0xaf, 0x2a, 0x4d, 0xb3, 0x83, 0x5d, 0x66, 0xe8, 0xf5, 0x60, 0x5c, - 0x25, 0x06, 0xe6, 0xe9, 0x1a, 0xc2, 0xa9, 0x31, 0xb6, 0xb6, 0xbb, 0xcf, 0xf5, 0xa0, 0x94, 0xcf, - 0xcb, 0x09, 0x4a, 0x38, 0x45, 0xdb, 0x88, 0x48, 0x3b, 0xde, 0x31, 0x22, 0xed, 0x79, 0x18, 0x0c, - 0x5b, 0xb7, 0x6a, 0x7e, 0xc3, 0x71, 0x3d, 0x66, 0x71, 0xa3, 0x3d, 0xdc, 0x2b, 0x12, 0x80, 0x63, - 0x1c, 0xb4, 0x04, 0x03, 0x8e, 0xd4, 0x2c, 0xa3, 0xfc, 0x60, 0x7b, 0x4a, 0x9f, 0xcc, 0xe3, 0x4f, - 0x49, 0x5d, 0xb2, 0xaa, 0x8b, 0x5e, 0x86, 0x11, 0x11, 0xd0, 0x43, 0xe4, 0xd2, 0x9f, 0x34, 0xdd, - 0x97, 0x2b, 0x3a, 0x10, 0x9b, 0xb8, 0xe8, 0x3a, 0x0c, 0x45, 0x7e, 0x9d, 0xf9, 0xe0, 0x52, 0x2e, - 0xf9, 0x58, 0x7e, 0x4c, 0xdc, 0x35, 0x85, 0xa6, 0xeb, 0x3c, 0x54, 0x55, 0xac, 0xd3, 0x41, 0x6b, - 0x7c, 0xbd, 0xb3, 0xb4, 0x45, 0x24, 0x14, 0xc9, 0xd8, 0x4f, 0xe5, 0x99, 0x4b, 0x32, 0x34, 0x73, - 0x3b, 0x88, 0x9a, 0x58, 0x27, 0x83, 0x2e, 0xc1, 0x44, 0x33, 0x70, 0x7d, 0xb6, 0x26, 0x94, 0xa6, - 0x7c, 0xca, 0x4c, 0x52, 0x5a, 0x4e, 0x22, 0xe0, 0x74, 0x1d, 0x16, 0x8f, 0x45, 0x14, 0x4e, 0x9d, - 0xe0, 0x89, 0xd6, 0xb8, 0x1c, 0x84, 0x97, 0x61, 0x05, 0x45, 0x2b, 0xec, 0x24, 0xe6, 0x22, 0xbc, - 0xa9, 0xe9, 0x7c, 0x2f, 0x7f, 0x5d, 0xd4, 0xc7, 0x79, 0x7f, 0xf5, 0x17, 0xc7, 0x14, 0x50, 0x4d, - 0xcb, 0x6f, 0x4e, 0x5f, 0x50, 0xe1, 0xd4, 0xc9, 0x36, 0xf6, 0xba, 0x89, 0xe7, 0x72, 0xcc, 0x10, - 0x18, 0xc5, 0x21, 0x4e, 0xd0, 0x44, 0xaf, 0xc1, 0xb8, 0x08, 0x56, 0x10, 0x0f, 0xd3, 0xa9, 0xd8, - 0xa7, 0x09, 0x27, 0x60, 0x38, 0x85, 0xcd, 0x13, 0x9d, 0x39, 0xb7, 0xea, 0x44, 0x1c, 0x7d, 0x57, - 0x5d, 0x6f, 0x2b, 0x9c, 0x3a, 0xcd, 0xce, 0x07, 0x91, 0xe8, 0x2c, 0x09, 0xc5, 0x19, 0x35, 0xd0, - 0x1a, 0x8c, 0x37, 0x03, 0x42, 0x1a, 0xec, 0x9d, 0x24, 0xee, 0xb3, 0x12, 0x0f, 0x47, 0x44, 0x7b, - 0x52, 0x4e, 0xc0, 0xf6, 0x33, 0xca, 0x70, 0x8a, 0x02, 0xba, 0x0d, 0x03, 0xfe, 0x36, 0x09, 0x36, - 0x89, 0x53, 0x9b, 0x3a, 0xd3, 0xc6, 0xd3, 0x4e, 0x5c, 0x6e, 0xd7, 0x04, 0x6e, 0xc2, 0x10, 0x49, - 0x16, 0x77, 0x36, 0x44, 0x92, 0x8d, 0xa1, 0xbf, 0x67, 0xc1, 0x09, 0xa9, 0xda, 0xab, 0x34, 0xe9, - 0xa8, 0xcf, 0xfb, 0x5e, 0x18, 0x05, 0x3c, 0x80, 0xce, 0xc3, 0xf9, 0x41, 0x65, 0xd6, 0x72, 0x2a, - 0x29, 0x2d, 0xc2, 0x89, 0x3c, 0x8c, 0x10, 0xe7, 0xb7, 0x48, 0x5f, 0xf6, 0x21, 0x89, 0xe4, 0x61, - 0x34, 0x1b, 0x2e, 0xbd, 0xb1, 0xb0, 0x3a, 0xf5, 0x08, 0x8f, 0xfe, 0x43, 0x37, 0x43, 0x25, 0x09, - 0xc4, 0x69, 0x7c, 0x74, 0x01, 0x0a, 0x7e, 0x38, 0xf5, 0x68, 0x9b, 0x94, 0xf8, 0x7e, 0xed, 0x5a, - 0x85, 0x1b, 0xa4, 0x5e, 0xab, 0xe0, 0x82, 0x1f, 0xca, 0x64, 0x63, 0xf4, 0x39, 0x1b, 0x4e, 0x3d, - 0xc6, 0x65, 0xce, 0x32, 0xd9, 0x18, 0x2b, 0xc4, 0x31, 0x1c, 0x6d, 0xc2, 0x58, 0x68, 0x88, 0x0d, - 0xc2, 0xa9, 0xb3, 0x6c, 0xa4, 0x1e, 0xcb, 0x9b, 0x34, 0x03, 0x5b, 0xcb, 0x02, 0x64, 0x52, 0xc1, - 0x49, 0xb2, 0x7c, 0x77, 0x69, 0x82, 0x8b, 0x70, 0xea, 0xf1, 0x0e, 0xbb, 0x4b, 0x43, 0xd6, 0x77, - 0x97, 0x4e, 0x03, 0x27, 0x68, 0x4e, 0x7f, 0x07, 0x4c, 0xa4, 0xd8, 0xa5, 0x83, 0x38, 0x5f, 0x4c, - 0x6f, 0xc1, 0x88, 0xb1, 0x24, 0x1f, 0xa8, 0x6d, 0xce, 0xef, 0x0f, 0xc2, 0xa0, 0xb2, 0x99, 0x40, - 0xe7, 0x4d, 0x73, 0x9c, 0x13, 0x49, 0x73, 0x9c, 0x81, 0xb2, 0x5f, 0x33, 0x2c, 0x70, 0xd6, 0x32, - 0x82, 0xda, 0xe6, 0x1d, 0x80, 0xdd, 0x7b, 0x88, 0x69, 0x7a, 0xa0, 0x62, 0xd7, 0x76, 0x3d, 0x3d, - 0x6d, 0x55, 0x4b, 0x97, 0x60, 0xc2, 0xf3, 0x19, 0x8f, 0x4e, 0x6a, 0x92, 0x01, 0x63, 0x7c, 0xd6, - 0xa0, 0x1e, 0x74, 0x2d, 0x81, 0x80, 0xd3, 0x75, 0x68, 0x83, 0x9c, 0x51, 0x4a, 0xea, 0xb2, 0x38, - 0x1f, 0x85, 0x05, 0x94, 0xbe, 0x0d, 0xf9, 0xaf, 0x70, 0x6a, 0x3c, 0xff, 0x6d, 0xc8, 0x2b, 0x25, - 0x99, 0xb1, 0x50, 0x32, 0x63, 0x4c, 0x75, 0xd3, 0xf4, 0x6b, 0xcb, 0x65, 0xc1, 0xe6, 0x6b, 0xe1, - 0xe6, 0x6b, 0xcb, 0x65, 0xcc, 0x61, 0x68, 0x16, 0xfa, 0xd8, 0x0f, 0x19, 0xcc, 0x26, 0x6f, 0x9b, - 0x2e, 0x97, 0xb5, 0x64, 0xa7, 0xac, 0x02, 0x16, 0x15, 0x99, 0x68, 0x9e, 0xbe, 0x8d, 0x98, 0x68, - 0xbe, 0xff, 0x1e, 0x45, 0xf3, 0x92, 0x00, 0x8e, 0x69, 0xa1, 0x3b, 0x70, 0xd4, 0x78, 0x8f, 0x2a, - 0x97, 0x39, 0xc8, 0xd7, 0xda, 0x27, 0x90, 0xe7, 0x4e, 0x89, 0x4e, 0x1f, 0x5d, 0xce, 0xa2, 0x84, - 0xb3, 0x1b, 0x40, 0x75, 0x98, 0xa8, 0xa6, 0x5a, 0x1d, 0xe8, 0xbe, 0x55, 0xb5, 0x2e, 0xd2, 0x2d, - 0xa6, 0x09, 0xa3, 0x97, 0x61, 0xe0, 0x6d, 0x9f, 0x5b, 0xd8, 0x89, 0xa7, 0x89, 0x0c, 0xd7, 0x32, - 0xf0, 0xc6, 0xb5, 0x0a, 0x2b, 0xdf, 0xdf, 0x2d, 0x0d, 0x95, 0xfd, 0x9a, 0xfc, 0x8b, 0x55, 0x05, - 0xf4, 0x03, 0x16, 0x4c, 0xa7, 0x1f, 0xbc, 0xaa, 0xd3, 0x23, 0xdd, 0x77, 0xda, 0x16, 0x8d, 0x4e, - 0x2f, 0xe6, 0x92, 0xc3, 0x6d, 0x9a, 0x42, 0x1f, 0xa6, 0xfb, 0x29, 0x74, 0xef, 0x12, 0x91, 0x29, - 0xfe, 0xe1, 0x78, 0x3f, 0xd1, 0xd2, 0xfd, 0xdd, 0xd2, 0x18, 0x3f, 0x19, 0xdd, 0xbb, 0x2a, 0x30, - 0x3e, 0xaf, 0x80, 0xbe, 0x1b, 0x8e, 0x06, 0x69, 0x01, 0x34, 0x91, 0x4c, 0xf8, 0x93, 0xdd, 0x9c, - 0xb2, 0xc9, 0x09, 0xc7, 0x59, 0x04, 0x71, 0x76, 0x3b, 0xf6, 0xaf, 0x5b, 0x4c, 0xf1, 0x20, 0xba, - 0x45, 0xc2, 0x56, 0x3d, 0x3a, 0x04, 0xab, 0xb6, 0x45, 0x43, 0xf1, 0x7f, 0xcf, 0x66, 0x69, 0xff, - 0xda, 0x62, 0x66, 0x69, 0x87, 0xe8, 0x60, 0xf7, 0x06, 0x0c, 0x44, 0xa2, 0x35, 0xd1, 0xf5, 0x3c, - 0x13, 0x1a, 0xd9, 0x29, 0x66, 0x9a, 0xa7, 0x1e, 0x39, 0xb2, 0x14, 0x2b, 0x32, 0xf6, 0xbf, 0xe0, - 0x33, 0x20, 0x21, 0x87, 0xa0, 0x5f, 0x5d, 0x30, 0xf5, 0xab, 0xa5, 0x0e, 0x5f, 0x90, 0xa3, 0x67, - 0xfd, 0xe7, 0x66, 0xbf, 0x99, 0x70, 0xef, 0xdd, 0x6e, 0x0f, 0x69, 0x7f, 0xde, 0x02, 0x88, 0x33, - 0x91, 0x74, 0x91, 0x19, 0xfa, 0x22, 0x7d, 0xd6, 0xf8, 0x91, 0x5f, 0xf5, 0xeb, 0x42, 0xbf, 0x73, - 0x32, 0x56, 0xf1, 0xf2, 0xf2, 0x7d, 0xed, 0x37, 0x56, 0xd8, 0xa8, 0x24, 0x43, 0x03, 0x17, 0x63, - 0xa3, 0x03, 0x23, 0x2c, 0xf0, 0x97, 0x2c, 0x38, 0x92, 0xe5, 0xad, 0x41, 0x1f, 0xc9, 0x5c, 0xcc, - 0xa9, 0x6c, 0x55, 0xd5, 0x6c, 0xde, 0x10, 0xe5, 0x58, 0x61, 0x74, 0x9d, 0x72, 0xfb, 0x60, 0x59, - 0x32, 0xae, 0xc1, 0x48, 0x39, 0x20, 0x1a, 0x7f, 0xf1, 0x6a, 0x9c, 0xc0, 0x67, 0x70, 0xee, 0xe9, - 0x03, 0x87, 0x40, 0xb2, 0xbf, 0x5c, 0x80, 0x23, 0xdc, 0xe2, 0x6a, 0x76, 0xdb, 0x77, 0x6b, 0x65, - 0xbf, 0x26, 0x7c, 0x6c, 0xdf, 0x84, 0xe1, 0xa6, 0x26, 0x9b, 0x6e, 0x17, 0xf1, 0x5d, 0x97, 0x61, - 0xc7, 0xd2, 0x34, 0xbd, 0x14, 0x1b, 0xb4, 0x50, 0x0d, 0x86, 0xc9, 0xb6, 0x5b, 0x55, 0x66, 0x3b, - 0x85, 0x03, 0x5f, 0xd2, 0xaa, 0x95, 0x45, 0x8d, 0x0e, 0x36, 0xa8, 0x76, 0x6d, 0x27, 0xad, 0xb1, - 0x68, 0x3d, 0x1d, 0x4c, 0x75, 0x7e, 0xdc, 0x82, 0xe3, 0x39, 0xf1, 0xe1, 0x69, 0x73, 0xb7, 0x99, - 0x6d, 0x9b, 0x58, 0xb6, 0xaa, 0x39, 0x6e, 0xf1, 0x86, 0x05, 0x14, 0x7d, 0x14, 0xa0, 0x19, 0xe7, - 0xc6, 0xec, 0x10, 0x48, 0xdb, 0x08, 0xa9, 0xab, 0x45, 0x47, 0x55, 0x29, 0x34, 0x35, 0x5a, 0xf6, - 0x97, 0x7a, 0xa0, 0x97, 0x59, 0x48, 0xa1, 0x32, 0xf4, 0x6f, 0xf2, 0xe0, 0x7d, 0x6d, 0xe7, 0x8d, - 0xe2, 0xca, 0x68, 0x80, 0xf1, 0xbc, 0x69, 0xa5, 0x58, 0x92, 0x41, 0x2b, 0x30, 0xc9, 0xf3, 0x7e, - 0xd6, 0x17, 0x48, 0xdd, 0xd9, 0x91, 0x62, 0xdf, 0x02, 0xfb, 0x54, 0x25, 0xfe, 0x5e, 0x4e, 0xa3, - 0xe0, 0xac, 0x7a, 0xe8, 0x55, 0x18, 0xa5, 0xcf, 0x70, 0xbf, 0x15, 0x49, 0x4a, 0x3c, 0xd1, 0xa6, - 0x7a, 0x99, 0xac, 0x19, 0x50, 0x9c, 0xc0, 0x46, 0x2f, 0xc3, 0x48, 0x33, 0x25, 0xe0, 0xee, 0x8d, - 0x25, 0x41, 0xa6, 0x50, 0xdb, 0xc4, 0x65, 0x0e, 0x1b, 0x2d, 0xe6, 0x9e, 0xb2, 0xb6, 0x19, 0x90, - 0x70, 0xd3, 0xaf, 0xd7, 0x18, 0x07, 0xdc, 0xab, 0x39, 0x6c, 0x24, 0xe0, 0x38, 0x55, 0x83, 0x52, - 0x59, 0x77, 0xdc, 0x7a, 0x2b, 0x20, 0x31, 0x95, 0x3e, 0x93, 0xca, 0x52, 0x02, 0x8e, 0x53, 0x35, - 0x3a, 0x4b, 0xee, 0xfb, 0xef, 0x8f, 0xe4, 0xde, 0xfe, 0x99, 0x02, 0x18, 0x53, 0xfb, 0x1e, 0x4e, - 0x00, 0xfa, 0x0a, 0xf4, 0x6c, 0x04, 0xcd, 0xaa, 0xb0, 0x06, 0xcc, 0xfc, 0xb2, 0x4b, 0xb8, 0x3c, - 0xaf, 0x7f, 0x19, 0xfd, 0x8f, 0x59, 0x2d, 0xba, 0xc7, 0x8f, 0x96, 0x03, 0x9f, 0x5e, 0x72, 0x32, - 0xbe, 0xa7, 0xf2, 0x8b, 0xea, 0x97, 0x11, 0x3e, 0xda, 0x44, 0xc2, 0x16, 0xce, 0x1d, 0x9c, 0x82, - 0x61, 0x38, 0x57, 0x11, 0x71, 0x7c, 0x24, 0x15, 0x74, 0x01, 0x86, 0x44, 0x4e, 0x46, 0xe6, 0xbe, - 0xc3, 0x37, 0x13, 0x33, 0xf4, 0x5b, 0x88, 0x8b, 0xb1, 0x8e, 0x63, 0xff, 0x60, 0x01, 0x26, 0x33, - 0xfc, 0x2f, 0xf9, 0x35, 0xb2, 0xe1, 0x86, 0x51, 0xb0, 0x93, 0xbc, 0x9c, 0xb0, 0x28, 0xc7, 0x0a, - 0x83, 0x9e, 0x55, 0xfc, 0xa2, 0x4a, 0x5e, 0x4e, 0xc2, 0xbf, 0x49, 0x40, 0x0f, 0x76, 0x39, 0xd1, - 0x6b, 0xbb, 0x15, 0x12, 0x19, 0x74, 0x5f, 0x5d, 0xdb, 0xcc, 0x2a, 0x80, 0x41, 0xe8, 0x13, 0x70, - 0x43, 0xa9, 0xba, 0xb5, 0x27, 0x20, 0x57, 0x76, 0x73, 0x18, 0xed, 0x5c, 0x44, 0x3c, 0xc7, 0x8b, - 0xc4, 0x43, 0x31, 0x0e, 0xc6, 0xcc, 0x4a, 0xb1, 0x80, 0xda, 0x5f, 0x2c, 0xc2, 0x89, 0x5c, 0x8f, - 0x6c, 0xda, 0xf5, 0x86, 0xef, 0xb9, 0x91, 0xaf, 0x2c, 0x28, 0x79, 0x00, 0x66, 0xd2, 0xdc, 0x5c, - 0x11, 0xe5, 0x58, 0x61, 0xa0, 0xb3, 0xd0, 0xcb, 0x84, 0xe2, 0xc9, 0x7c, 0x6c, 0x78, 0x6e, 0x81, - 0x87, 0xb6, 0xe4, 0x60, 0xed, 0x56, 0x2f, 0xb6, 0xbd, 0xd5, 0x1f, 0xa1, 0x1c, 0x8c, 0x5f, 0x4f, - 0x5e, 0x28, 0xb4, 0xbb, 0xbe, 0x5f, 0xc7, 0x0c, 0x88, 0x1e, 0x13, 0xe3, 0x95, 0x30, 0x19, 0xc4, - 0x4e, 0xcd, 0x0f, 0xb5, 0x41, 0x7b, 0x02, 0xfa, 0xb7, 0xc8, 0x4e, 0xe0, 0x7a, 0x1b, 0x49, 0x53, - 0xd2, 0x2b, 0xbc, 0x18, 0x4b, 0xb8, 0x99, 0x7e, 0xbc, 0xff, 0x7e, 0xa4, 0x1f, 0xd7, 0x57, 0xc0, - 0x40, 0x47, 0xf6, 0xe4, 0x87, 0x8a, 0x30, 0x86, 0xe7, 0x16, 0xde, 0x9f, 0x88, 0xeb, 0xe9, 0x89, - 0xb8, 0x1f, 0x59, 0xba, 0x0f, 0x36, 0x1b, 0xbf, 0x62, 0xc1, 0x18, 0xcb, 0x0c, 0x29, 0xc2, 0xa9, - 0xb8, 0xbe, 0x77, 0x08, 0x4f, 0x81, 0x47, 0xa0, 0x37, 0xa0, 0x8d, 0x8a, 0x19, 0x54, 0x7b, 0x9c, - 0xf5, 0x04, 0x73, 0x18, 0x3a, 0x09, 0x3d, 0xac, 0x0b, 0x74, 0xf2, 0x86, 0xf9, 0x11, 0xbc, 0xe0, - 0x44, 0x0e, 0x66, 0xa5, 0x2c, 0xb0, 0x23, 0x26, 0xcd, 0xba, 0xcb, 0x3b, 0x1d, 0x9b, 0x2c, 0xbc, - 0x3b, 0x62, 0xb5, 0x64, 0x76, 0xed, 0x9d, 0x05, 0x76, 0xcc, 0x26, 0xd9, 0xfe, 0x99, 0xfd, 0x97, - 0x05, 0x38, 0x9d, 0x59, 0xaf, 0xeb, 0xc0, 0x8e, 0xed, 0x6b, 0x3f, 0xc8, 0x3c, 0x72, 0xc5, 0x43, - 0x34, 0xd4, 0xef, 0xe9, 0x96, 0xfb, 0xef, 0xed, 0x22, 0xde, 0x62, 0xe6, 0x90, 0xbd, 0x4b, 0xe2, - 0x2d, 0x66, 0xf6, 0x2d, 0x47, 0x4c, 0xf0, 0x77, 0x85, 0x9c, 0x6f, 0x61, 0x02, 0x83, 0x73, 0xf4, - 0x9c, 0x61, 0xc0, 0x50, 0x3e, 0xc2, 0xf9, 0x19, 0xc3, 0xcb, 0xb0, 0x82, 0xa2, 0x59, 0x18, 0x6b, - 0xb8, 0x1e, 0x3d, 0x7c, 0x76, 0x4c, 0x56, 0x5c, 0xe9, 0x32, 0x56, 0x4c, 0x30, 0x4e, 0xe2, 0x23, - 0x57, 0x8b, 0xc5, 0xc8, 0xbf, 0xee, 0xe5, 0x03, 0xed, 0xba, 0x19, 0xd3, 0x9c, 0x43, 0x8d, 0x62, - 0x46, 0x5c, 0xc6, 0x15, 0x4d, 0x4e, 0x54, 0xec, 0x5e, 0x4e, 0x34, 0x9c, 0x2d, 0x23, 0x9a, 0x7e, - 0x19, 0x46, 0xee, 0x59, 0x37, 0x62, 0x7f, 0xa3, 0x08, 0x0f, 0xb5, 0xd9, 0xf6, 0xfc, 0xac, 0x37, - 0xe6, 0x40, 0x3b, 0xeb, 0x53, 0xf3, 0x50, 0x86, 0x23, 0xeb, 0xad, 0x7a, 0x7d, 0x87, 0x79, 0xa4, - 0x91, 0x9a, 0xc4, 0x10, 0x3c, 0xa5, 0x14, 0x8e, 0x1c, 0x59, 0xca, 0xc0, 0xc1, 0x99, 0x35, 0xe9, - 0x13, 0x8b, 0xde, 0x24, 0x3b, 0x8a, 0x54, 0xe2, 0x89, 0x85, 0x75, 0x20, 0x36, 0x71, 0xd1, 0x25, - 0x98, 0x70, 0xb6, 0x1d, 0x97, 0xe7, 0xe1, 0x90, 0x04, 0xf8, 0x1b, 0x4b, 0xc9, 0xa2, 0x67, 0x93, - 0x08, 0x38, 0x5d, 0x07, 0xbd, 0x0e, 0xc8, 0xbf, 0xc5, 0xbc, 0x5c, 0x6a, 0x97, 0x88, 0x27, 0xb4, - 0xee, 0x6c, 0xee, 0x8a, 0xf1, 0x91, 0x70, 0x2d, 0x85, 0x81, 0x33, 0x6a, 0x25, 0xa2, 0x02, 0xf6, - 0xe5, 0x47, 0x05, 0x6c, 0x7f, 0x2e, 0x76, 0x4c, 0x61, 0xf8, 0x16, 0x8c, 0x1c, 0xd4, 0x2c, 0xfb, - 0x09, 0xe8, 0x0f, 0x44, 0x72, 0xf8, 0x84, 0xfb, 0xb7, 0x4c, 0x9d, 0x2d, 0xe1, 0xf6, 0x7f, 0xb1, - 0x40, 0xc9, 0x92, 0xcd, 0x00, 0xe0, 0x2f, 0x33, 0x1b, 0x73, 0x2e, 0x05, 0xd7, 0x62, 0x7e, 0x1d, - 0xd5, 0x6c, 0xcc, 0x63, 0x20, 0x36, 0x71, 0xf9, 0x72, 0x0b, 0xe3, 0x50, 0x13, 0xc6, 0x03, 0x42, - 0xc4, 0x27, 0x55, 0x18, 0xe8, 0x63, 0xd0, 0x5f, 0x73, 0xb7, 0xdd, 0x50, 0xc8, 0xd1, 0x0e, 0xac, - 0xb7, 0x8b, 0xbf, 0x6f, 0x81, 0x93, 0xc1, 0x92, 0x9e, 0xfd, 0x23, 0x16, 0x28, 0xbd, 0xe4, 0x65, - 0xe2, 0xd4, 0xa3, 0x4d, 0xf4, 0x1a, 0x80, 0xa4, 0xa0, 0x64, 0x6f, 0xd2, 0x5a, 0x0a, 0xb0, 0x82, - 0xec, 0x1b, 0xff, 0xb0, 0x56, 0x07, 0xbd, 0x0a, 0x7d, 0x9b, 0x8c, 0x96, 0xf8, 0xb6, 0xb3, 0x4a, - 0xd5, 0xc5, 0x4a, 0xf7, 0x77, 0x4b, 0x47, 0xcc, 0x36, 0xe5, 0x2d, 0xc6, 0x6b, 0xd9, 0x3f, 0x54, - 0x88, 0xe7, 0xf4, 0x8d, 0x96, 0x1f, 0x39, 0x87, 0xc0, 0x89, 0x5c, 0x32, 0x38, 0x91, 0xc7, 0xb2, - 0x17, 0xaa, 0xd6, 0xa5, 0x5c, 0x0e, 0xe4, 0x5a, 0x82, 0x03, 0x79, 0xbc, 0x33, 0xa9, 0xf6, 0x9c, - 0xc7, 0xbf, 0xb4, 0x60, 0xc2, 0xc0, 0x3f, 0x84, 0x0b, 0x70, 0xc9, 0xbc, 0x00, 0x1f, 0xee, 0xf8, - 0x0d, 0x39, 0x17, 0xdf, 0xf7, 0x17, 0x13, 0x7d, 0x67, 0x17, 0xde, 0xdb, 0xd0, 0xb3, 0xe9, 0x04, - 0x35, 0xf1, 0xae, 0x3f, 0xdf, 0xd5, 0x58, 0xcf, 0x5c, 0x76, 0x02, 0x61, 0x69, 0xf1, 0xb4, 0x1c, - 0x75, 0x5a, 0xd4, 0xd1, 0xca, 0x82, 0x35, 0x85, 0x2e, 0x42, 0x5f, 0x58, 0xf5, 0x9b, 0xca, 0x61, - 0x8f, 0xe5, 0xf5, 0xae, 0xb0, 0x92, 0xfd, 0xdd, 0x12, 0x32, 0x9b, 0xa3, 0xc5, 0x58, 0xe0, 0xa3, - 0x37, 0x61, 0x84, 0xfd, 0x52, 0x66, 0x8f, 0xc5, 0x7c, 0x09, 0x4c, 0x45, 0x47, 0xe4, 0x36, 0xc1, - 0x46, 0x11, 0x36, 0x49, 0x4d, 0x6f, 0xc0, 0xa0, 0xfa, 0xac, 0x07, 0xaa, 0xad, 0xff, 0x8f, 0x45, - 0x98, 0xcc, 0x58, 0x73, 0x28, 0x34, 0x66, 0xe2, 0x42, 0x97, 0x4b, 0xf5, 0x1d, 0xce, 0x45, 0xc8, - 0x1e, 0x80, 0x35, 0xb1, 0xb6, 0xba, 0x6e, 0xf4, 0x7a, 0x48, 0x92, 0x8d, 0xd2, 0xa2, 0xce, 0x8d, - 0xd2, 0xc6, 0x0e, 0x6d, 0xa8, 0x69, 0x43, 0xaa, 0xa7, 0x0f, 0x74, 0x4e, 0x7f, 0xab, 0x07, 0x8e, - 0x64, 0x05, 0xb3, 0x46, 0x9f, 0x81, 0x3e, 0xe6, 0x51, 0x26, 0x05, 0x67, 0xcf, 0xb7, 0x1b, 0x61, - 0xbd, 0xe6, 0x0c, 0x73, 0x4a, 0x13, 0x31, 0x64, 0x67, 0xe4, 0x71, 0xc4, 0x0b, 0x3b, 0x0e, 0xb3, - 0x68, 0x93, 0xc5, 0x76, 0x12, 0xb7, 0xa7, 0x3c, 0x3e, 0x3e, 0xd4, 0x75, 0x07, 0xc4, 0xfd, 0x1b, - 0x26, 0x4c, 0xaa, 0x64, 0x71, 0x67, 0x93, 0x2a, 0xd9, 0x32, 0x5a, 0x86, 0xbe, 0x2a, 0xb7, 0xd5, - 0x29, 0x76, 0x3e, 0xc2, 0xb8, 0xa1, 0x8e, 0x3a, 0x80, 0x85, 0x81, 0x8e, 0x20, 0x30, 0xed, 0xc2, - 0x90, 0x36, 0x30, 0x0f, 0x74, 0xf1, 0x6c, 0xd1, 0x8b, 0x4f, 0x1b, 0x82, 0x07, 0xba, 0x80, 0x7e, - 0x4c, 0xbb, 0xfb, 0xc5, 0x79, 0xf0, 0x41, 0x83, 0x77, 0x3a, 0x99, 0xf0, 0xf3, 0x4b, 0xec, 0x2b, - 0xc6, 0x4b, 0x55, 0xcc, 0xe0, 0xeb, 0xb9, 0x39, 0x9c, 0xcc, 0x0b, 0xbf, 0x7d, 0xc0, 0x75, 0xfb, - 0xc7, 0x2d, 0x48, 0x78, 0x62, 0x29, 0x71, 0xa7, 0x95, 0x2b, 0xee, 0x3c, 0x03, 0x3d, 0x81, 0x5f, - 0x27, 0xc9, 0x1c, 0xf9, 0xd8, 0xaf, 0x13, 0xcc, 0x20, 0x14, 0x23, 0x8a, 0x85, 0x58, 0xc3, 0xfa, - 0x03, 0x5d, 0x3c, 0xbd, 0x1f, 0x81, 0xde, 0x3a, 0xd9, 0x26, 0xf5, 0x64, 0x2a, 0xd3, 0xab, 0xb4, - 0x10, 0x73, 0x98, 0xfd, 0x2b, 0x3d, 0x70, 0xaa, 0x6d, 0x08, 0x38, 0xca, 0x60, 0x6e, 0x38, 0x11, - 0xb9, 0xed, 0xec, 0x24, 0x53, 0xf8, 0x5d, 0xe2, 0xc5, 0x58, 0xc2, 0x99, 0x57, 0x34, 0x4f, 0x69, - 0x93, 0x10, 0x0e, 0x8b, 0x4c, 0x36, 0x02, 0x6a, 0x0a, 0x1b, 0x8b, 0xf7, 0x43, 0xd8, 0xf8, 0x2c, - 0x40, 0x18, 0xd6, 0xb9, 0xc1, 0x65, 0x4d, 0xb8, 0x5b, 0xc7, 0xa9, 0x8f, 0x2a, 0x57, 0x05, 0x04, - 0x6b, 0x58, 0x68, 0x01, 0xc6, 0x9b, 0x81, 0x1f, 0x71, 0x59, 0xfb, 0x02, 0xb7, 0x49, 0xee, 0x35, - 0xa3, 0x6f, 0x95, 0x13, 0x70, 0x9c, 0xaa, 0x81, 0x5e, 0x80, 0x21, 0x11, 0x91, 0xab, 0xec, 0xfb, - 0x75, 0x21, 0xde, 0x53, 0x66, 0xba, 0x95, 0x18, 0x84, 0x75, 0x3c, 0xad, 0x1a, 0x13, 0xe0, 0xf7, - 0x67, 0x56, 0xe3, 0x42, 0x7c, 0x0d, 0x2f, 0x11, 0xbd, 0x7f, 0xa0, 0xab, 0xe8, 0xfd, 0xb1, 0xc0, - 0x73, 0xb0, 0x6b, 0x7d, 0x32, 0x74, 0x14, 0x11, 0x7e, 0xa5, 0x07, 0x26, 0xc5, 0xc2, 0x79, 0xd0, - 0xcb, 0xe5, 0x7a, 0x7a, 0xb9, 0xdc, 0x0f, 0x91, 0xe8, 0xfb, 0x6b, 0xe6, 0xb0, 0xd7, 0xcc, 0x0f, - 0x5b, 0x60, 0xf2, 0x90, 0xe8, 0xff, 0xcb, 0xcd, 0x81, 0xfa, 0x42, 0x2e, 0x4f, 0x1a, 0x87, 0xf6, - 0x7e, 0x67, 0xd9, 0x50, 0xed, 0xff, 0x64, 0xc1, 0xc3, 0x1d, 0x29, 0xa2, 0x45, 0x18, 0x64, 0x8c, - 0xae, 0xf6, 0x2e, 0x7e, 0x5c, 0xf9, 0x2c, 0x48, 0x40, 0x0e, 0xdf, 0x1d, 0xd7, 0x44, 0x8b, 0xa9, - 0x64, 0xb3, 0x4f, 0x64, 0x24, 0x9b, 0x3d, 0x6a, 0x0c, 0xcf, 0x3d, 0x66, 0x9b, 0xfd, 0x02, 0xbd, - 0x71, 0x4c, 0xc7, 0xc7, 0x0f, 0x19, 0xe2, 0x5c, 0x3b, 0x21, 0xce, 0x45, 0x26, 0xb6, 0x76, 0x87, - 0xbc, 0x06, 0xe3, 0x2c, 0x54, 0x27, 0xf3, 0xa0, 0x11, 0x1e, 0x93, 0x85, 0xd8, 0x4a, 0xfe, 0x6a, - 0x02, 0x86, 0x53, 0xd8, 0xf6, 0x9f, 0x17, 0xa1, 0x8f, 0x6f, 0xbf, 0x43, 0x78, 0xf8, 0x3e, 0x05, - 0x83, 0x6e, 0xa3, 0xd1, 0xe2, 0xf9, 0x43, 0x7b, 0x63, 0x9b, 0xeb, 0x65, 0x59, 0x88, 0x63, 0x38, - 0x5a, 0x12, 0x9a, 0x84, 0x36, 0xd1, 0xc0, 0x79, 0xc7, 0x67, 0x16, 0x9c, 0xc8, 0xe1, 0x5c, 0x9c, - 0xba, 0x67, 0x63, 0x9d, 0x03, 0xfa, 0x24, 0x40, 0x18, 0x05, 0xae, 0xb7, 0x41, 0xcb, 0x44, 0xca, - 0x88, 0x27, 0xdb, 0x50, 0xab, 0x28, 0x64, 0x4e, 0x33, 0x3e, 0x73, 0x14, 0x00, 0x6b, 0x14, 0xd1, - 0x8c, 0x71, 0xd3, 0x4f, 0x27, 0xe6, 0x0e, 0x38, 0xd5, 0x78, 0xce, 0xa6, 0x5f, 0x84, 0x41, 0x45, - 0xbc, 0x93, 0x5c, 0x71, 0x58, 0x67, 0xd8, 0x3e, 0x02, 0x63, 0x89, 0xbe, 0x1d, 0x48, 0x2c, 0xf9, - 0xab, 0x16, 0x8c, 0xf1, 0xce, 0x2c, 0x7a, 0xdb, 0xe2, 0x36, 0xb8, 0x0b, 0x47, 0xea, 0x19, 0xa7, - 0xb2, 0x98, 0xfe, 0xee, 0x4f, 0x71, 0x25, 0x86, 0xcc, 0x82, 0xe2, 0xcc, 0x36, 0xd0, 0x39, 0xba, - 0xe3, 0xe8, 0xa9, 0xeb, 0xd4, 0x45, 0xd8, 0x8f, 0x61, 0xbe, 0xdb, 0x78, 0x19, 0x56, 0x50, 0xfb, - 0x8f, 0x2d, 0x98, 0xe0, 0x3d, 0xbf, 0x42, 0x76, 0xd4, 0xd9, 0xf4, 0xad, 0xec, 0xbb, 0xc8, 0x5c, - 0x5d, 0xc8, 0xc9, 0x5c, 0xad, 0x7f, 0x5a, 0xb1, 0xed, 0xa7, 0x7d, 0xd9, 0x02, 0xb1, 0x42, 0x0e, - 0x41, 0xd2, 0xf2, 0x1d, 0xa6, 0xa4, 0x65, 0x3a, 0x7f, 0x13, 0xe4, 0x88, 0x58, 0xfe, 0xd6, 0x82, - 0x71, 0x8e, 0x10, 0x5b, 0x41, 0x7c, 0x4b, 0xe7, 0x61, 0xce, 0xfc, 0xa2, 0x4c, 0xb3, 0xd6, 0x2b, - 0x64, 0x67, 0xcd, 0x2f, 0x3b, 0xd1, 0x66, 0xf6, 0x47, 0x19, 0x93, 0xd5, 0xd3, 0x76, 0xb2, 0x6a, - 0x72, 0x03, 0x19, 0x19, 0x12, 0x3b, 0x08, 0x80, 0x0f, 0x9a, 0x21, 0xd1, 0xfe, 0x0b, 0x0b, 0x10, - 0x6f, 0xc6, 0x60, 0xdc, 0x28, 0x3b, 0xc4, 0x4a, 0xb5, 0x8b, 0x2e, 0x3e, 0x9a, 0x14, 0x04, 0x6b, - 0x58, 0xf7, 0x65, 0x78, 0x12, 0xa6, 0x2c, 0xc5, 0xce, 0xa6, 0x2c, 0x07, 0x18, 0xd1, 0x2f, 0xf7, - 0x43, 0xd2, 0x67, 0x12, 0xdd, 0x80, 0xe1, 0xaa, 0xd3, 0x74, 0x6e, 0xb9, 0x75, 0x37, 0x72, 0x49, - 0xd8, 0xce, 0xce, 0x6d, 0x5e, 0xc3, 0x13, 0xc6, 0x07, 0x5a, 0x09, 0x36, 0xe8, 0xa0, 0x19, 0x80, - 0x66, 0xe0, 0x6e, 0xbb, 0x75, 0xb2, 0xc1, 0x04, 0x42, 0x2c, 0xd0, 0x10, 0x37, 0xba, 0x93, 0xa5, - 0x58, 0xc3, 0xc8, 0x88, 0xef, 0x51, 0x7c, 0xc0, 0xf1, 0x3d, 0xe0, 0xd0, 0xe2, 0x7b, 0xf4, 0x1c, - 0x28, 0xbe, 0xc7, 0xc0, 0x81, 0xe3, 0x7b, 0xf4, 0x76, 0x15, 0xdf, 0x03, 0xc3, 0x31, 0xc9, 0x7b, - 0xd2, 0xff, 0x4b, 0x6e, 0x9d, 0x88, 0x07, 0x07, 0x8f, 0x8e, 0x34, 0xbd, 0xb7, 0x5b, 0x3a, 0x86, - 0x33, 0x31, 0x70, 0x4e, 0x4d, 0xf4, 0x51, 0x98, 0x72, 0xea, 0x75, 0xff, 0xb6, 0x9a, 0xd4, 0xc5, - 0xb0, 0xea, 0xd4, 0xb9, 0x72, 0xa9, 0x9f, 0x51, 0x3d, 0xb9, 0xb7, 0x5b, 0x9a, 0x9a, 0xcd, 0xc1, - 0xc1, 0xb9, 0xb5, 0xd1, 0x2b, 0x30, 0xd8, 0x0c, 0xfc, 0xea, 0x8a, 0xe6, 0xd8, 0x7d, 0x9a, 0x0e, - 0x60, 0x59, 0x16, 0xee, 0xef, 0x96, 0x46, 0xd4, 0x1f, 0x76, 0xe1, 0xc7, 0x15, 0x32, 0x42, 0x67, - 0x0c, 0x3d, 0xe8, 0xd0, 0x19, 0xc3, 0xf7, 0x39, 0x74, 0x86, 0xbd, 0x05, 0x93, 0x15, 0x12, 0xb8, - 0x4e, 0xdd, 0xbd, 0x4b, 0x79, 0x72, 0x79, 0x06, 0xae, 0xc1, 0x60, 0x90, 0x38, 0xf5, 0xbb, 0x8a, - 0x02, 0xae, 0xc9, 0x65, 0xe4, 0x29, 0x1f, 0x13, 0xb2, 0xff, 0xb7, 0x05, 0xfd, 0xc2, 0x0f, 0xf3, - 0x10, 0x38, 0xd3, 0x59, 0x43, 0x25, 0x53, 0xca, 0x9e, 0x14, 0xd6, 0x99, 0x5c, 0x65, 0xcc, 0x72, - 0x42, 0x19, 0xf3, 0x70, 0x3b, 0x22, 0xed, 0xd5, 0x30, 0xff, 0xb0, 0x48, 0x5f, 0x08, 0x46, 0x44, - 0x80, 0x07, 0x3f, 0x04, 0xab, 0xd0, 0x1f, 0x0a, 0x8f, 0xf4, 0x42, 0xbe, 0x2f, 0x4f, 0x72, 0x12, - 0x63, 0x1b, 0x48, 0xe1, 0x83, 0x2e, 0x89, 0x64, 0xba, 0xba, 0x17, 0x1f, 0xa0, 0xab, 0x7b, 0xa7, - 0x98, 0x09, 0x3d, 0xf7, 0x23, 0x66, 0x82, 0xfd, 0x35, 0x76, 0x3b, 0xeb, 0xe5, 0x87, 0xc0, 0xb8, - 0x5d, 0x32, 0xef, 0x71, 0xbb, 0xcd, 0xca, 0x12, 0x9d, 0xca, 0x61, 0xe0, 0x7e, 0xd9, 0x82, 0x53, - 0x19, 0x5f, 0xa5, 0x71, 0x73, 0x4f, 0xc3, 0x80, 0xd3, 0xaa, 0xb9, 0x6a, 0x2f, 0x6b, 0xda, 0xe2, - 0x59, 0x51, 0x8e, 0x15, 0x06, 0x9a, 0x87, 0x09, 0x72, 0xa7, 0xe9, 0x72, 0x35, 0xbc, 0x6e, 0x3a, - 0x5e, 0xe4, 0xce, 0xbb, 0x8b, 0x49, 0x20, 0x4e, 0xe3, 0xab, 0xb8, 0x6b, 0xc5, 0xdc, 0xb8, 0x6b, - 0xbf, 0x60, 0xc1, 0x90, 0xf2, 0xc9, 0x7e, 0xe0, 0xa3, 0xfd, 0x9a, 0x39, 0xda, 0x0f, 0xb5, 0x19, - 0xed, 0x9c, 0x61, 0xfe, 0xa3, 0x82, 0xea, 0x6f, 0xd9, 0x0f, 0xa2, 0x2e, 0xb8, 0xc4, 0x7b, 0x77, - 0x7b, 0xb9, 0x00, 0x43, 0x4e, 0xb3, 0x29, 0x01, 0xd2, 0x7e, 0x91, 0xe5, 0x74, 0x88, 0x8b, 0xb1, - 0x8e, 0xa3, 0xbc, 0x70, 0x8a, 0xb9, 0x5e, 0x38, 0x35, 0x80, 0xc8, 0x09, 0x36, 0x48, 0x44, 0xcb, - 0x84, 0xb9, 0x75, 0xfe, 0x79, 0xd3, 0x8a, 0xdc, 0xfa, 0x8c, 0xeb, 0x45, 0x61, 0x14, 0xcc, 0x2c, - 0x7b, 0xd1, 0xb5, 0x80, 0x3f, 0x53, 0xb5, 0xe8, 0x86, 0x8a, 0x16, 0xd6, 0xe8, 0xca, 0xf8, 0x23, - 0xac, 0x8d, 0x5e, 0xd3, 0x10, 0x66, 0x55, 0x94, 0x63, 0x85, 0x61, 0xbf, 0xc8, 0x6e, 0x1f, 0x36, - 0xa6, 0x07, 0x8b, 0xda, 0xf7, 0x97, 0xc3, 0x6a, 0x36, 0x98, 0x4a, 0x78, 0x41, 0x8f, 0x0d, 0xd8, - 0xfe, 0xb0, 0xa7, 0x0d, 0xeb, 0xfe, 0xac, 0x71, 0x00, 0x41, 0xf4, 0xf1, 0x94, 0x71, 0xd3, 0x33, - 0x1d, 0x6e, 0x8d, 0x03, 0x98, 0x33, 0xb1, 0x04, 0x6f, 0x2c, 0xfd, 0xd5, 0x72, 0x59, 0xec, 0x0b, - 0x2d, 0xc1, 0x9b, 0x00, 0xe0, 0x18, 0x87, 0x32, 0x6c, 0xea, 0x4f, 0x38, 0x85, 0xe2, 0x38, 0xe0, - 0x0a, 0x3b, 0xc4, 0x1a, 0x06, 0x3a, 0x2f, 0x84, 0x16, 0x5c, 0xf7, 0xf0, 0x50, 0x42, 0x68, 0x21, - 0x87, 0x4b, 0x93, 0x34, 0x5d, 0x80, 0x21, 0x72, 0x27, 0x22, 0x81, 0xe7, 0xd4, 0x69, 0x0b, 0xbd, - 0x71, 0xe8, 0xda, 0xc5, 0xb8, 0x18, 0xeb, 0x38, 0x68, 0x0d, 0xc6, 0x42, 0x2e, 0xcb, 0x53, 0xd9, - 0x27, 0xb8, 0x4c, 0xf4, 0x49, 0xe5, 0x0d, 0x6f, 0x82, 0xf7, 0x59, 0x11, 0x3f, 0x9d, 0x64, 0x8c, - 0x90, 0x24, 0x09, 0xf4, 0x2a, 0x8c, 0xd6, 0x7d, 0xa7, 0x36, 0xe7, 0xd4, 0x1d, 0xaf, 0xca, 0xc6, - 0x67, 0xc0, 0x08, 0x14, 0x39, 0x7a, 0xd5, 0x80, 0xe2, 0x04, 0x36, 0x65, 0x10, 0xf5, 0x12, 0x91, - 0x31, 0xc5, 0xf1, 0x36, 0x48, 0x38, 0x35, 0xc8, 0xbe, 0x8a, 0x31, 0x88, 0x57, 0x73, 0x70, 0x70, - 0x6e, 0x6d, 0x74, 0x11, 0x86, 0xe5, 0xe7, 0x6b, 0x21, 0x75, 0x62, 0x87, 0x26, 0x0d, 0x86, 0x0d, - 0x4c, 0x14, 0xc2, 0x51, 0xf9, 0x7f, 0x2d, 0x70, 0xd6, 0xd7, 0xdd, 0xaa, 0x88, 0x33, 0xc1, 0x9d, - 0xbf, 0x3f, 0x22, 0x3d, 0x4d, 0x17, 0xb3, 0x90, 0xf6, 0x77, 0x4b, 0x27, 0xc5, 0xa8, 0x65, 0xc2, - 0x71, 0x36, 0x6d, 0xb4, 0x02, 0x93, 0xdc, 0x06, 0x66, 0x7e, 0x93, 0x54, 0xb7, 0xe4, 0x86, 0x63, - 0x5c, 0xa3, 0xe6, 0xf8, 0x73, 0x39, 0x8d, 0x82, 0xb3, 0xea, 0xa1, 0xb7, 0x60, 0xaa, 0xd9, 0xba, - 0x55, 0x77, 0xc3, 0xcd, 0x55, 0x3f, 0x62, 0x26, 0x64, 0xb3, 0xb5, 0x5a, 0x40, 0x42, 0xee, 0x1b, - 0xcc, 0xae, 0x5e, 0x19, 0x06, 0xa9, 0x9c, 0x83, 0x87, 0x73, 0x29, 0xa0, 0xbb, 0x70, 0x34, 0xb1, - 0x10, 0x44, 0x3c, 0x93, 0xd1, 0xfc, 0xdc, 0x53, 0x95, 0xac, 0x0a, 0x22, 0x34, 0x50, 0x16, 0x08, - 0x67, 0x37, 0x81, 0x5e, 0x02, 0x70, 0x9b, 0x4b, 0x4e, 0xc3, 0xad, 0xd3, 0xe7, 0xe8, 0x24, 0x5b, - 0x23, 0xf4, 0x69, 0x02, 0xcb, 0x65, 0x59, 0x4a, 0xcf, 0x66, 0xf1, 0x6f, 0x07, 0x6b, 0xd8, 0xe8, - 0x2a, 0x8c, 0x8a, 0x7f, 0x3b, 0x62, 0x4a, 0x27, 0x54, 0x9a, 0xd2, 0x51, 0x59, 0x43, 0xcd, 0x63, - 0xa2, 0x04, 0x27, 0xea, 0xa2, 0x0d, 0x38, 0x25, 0x73, 0xa4, 0xea, 0xeb, 0x53, 0xce, 0x41, 0xc8, - 0x12, 0x3e, 0x0d, 0x70, 0x9f, 0xa2, 0xd9, 0x76, 0x88, 0xb8, 0x3d, 0x1d, 0x7a, 0xaf, 0xeb, 0xcb, - 0x9c, 0x7b, 0x8c, 0x1f, 0x8d, 0xc3, 0x6d, 0x5e, 0x4d, 0x02, 0x71, 0x1a, 0x1f, 0xf9, 0x70, 0xd4, - 0xf5, 0xb2, 0x56, 0xf5, 0x31, 0x46, 0xe8, 0xc3, 0xdc, 0x59, 0xbe, 0xfd, 0x8a, 0xce, 0x84, 0xe3, - 0x6c, 0xba, 0x68, 0x19, 0x26, 0x23, 0x5e, 0xb0, 0xe0, 0x86, 0x3c, 0x9f, 0x0c, 0x7d, 0xf6, 0x1d, - 0x67, 0xcd, 0x1d, 0xa7, 0xab, 0x79, 0x2d, 0x0d, 0xc6, 0x59, 0x75, 0xde, 0x99, 0x01, 0xe8, 0xd7, - 0x2d, 0x5a, 0x5b, 0x63, 0xf4, 0xd1, 0xa7, 0x60, 0x58, 0x1f, 0x1f, 0xc1, 0xb4, 0x9c, 0xcd, 0xe6, - 0x83, 0xb5, 0xe3, 0x85, 0x3f, 0x13, 0xd4, 0x11, 0xa2, 0xc3, 0xb0, 0x41, 0x11, 0x55, 0x33, 0x82, - 0x5c, 0x9c, 0xef, 0x8e, 0x29, 0xea, 0xde, 0xfe, 0x91, 0x40, 0xf6, 0xce, 0x41, 0x57, 0x61, 0xa0, - 0x5a, 0x77, 0x89, 0x17, 0x2d, 0x97, 0xdb, 0x45, 0x41, 0x9d, 0x17, 0x38, 0x62, 0x2b, 0x8a, 0x34, - 0x50, 0xbc, 0x0c, 0x2b, 0x0a, 0xf6, 0x45, 0x18, 0xaa, 0xd4, 0x09, 0x69, 0x72, 0x3f, 0x2e, 0xf4, - 0x04, 0x7b, 0x98, 0x30, 0xd6, 0xd2, 0x62, 0xac, 0xa5, 0xfe, 0xe6, 0x60, 0x4c, 0xa5, 0x84, 0xdb, - 0xbf, 0x53, 0x80, 0x52, 0x87, 0x6c, 0x64, 0x09, 0x7d, 0x9b, 0xd5, 0x95, 0xbe, 0x6d, 0x16, 0xc6, - 0xe2, 0x7f, 0xba, 0x28, 0x4f, 0x19, 0x43, 0xdf, 0x30, 0xc1, 0x38, 0x89, 0xdf, 0xb5, 0x5f, 0x8b, - 0xae, 0xb2, 0xeb, 0xe9, 0xe8, 0x99, 0x65, 0xa8, 0xea, 0x7b, 0xbb, 0x7f, 0x7b, 0xe7, 0xaa, 0x5d, - 0xed, 0xaf, 0x15, 0xe0, 0xa8, 0x1a, 0xc2, 0xf7, 0xee, 0xc0, 0x5d, 0x4f, 0x0f, 0xdc, 0x7d, 0x50, - 0x5a, 0xdb, 0xd7, 0xa0, 0x8f, 0x87, 0x66, 0xed, 0x82, 0xe7, 0x7f, 0xc4, 0x8c, 0x92, 0xaf, 0xd8, - 0x4c, 0x23, 0x52, 0xfe, 0x0f, 0x58, 0x30, 0x96, 0x70, 0x90, 0x44, 0x58, 0xf3, 0xa2, 0xbf, 0x17, - 0xbe, 0x3c, 0x8b, 0xe3, 0x3f, 0x03, 0x3d, 0x9b, 0xbe, 0x32, 0x52, 0x56, 0x18, 0x97, 0xfd, 0x30, - 0xc2, 0x0c, 0x62, 0xff, 0x89, 0x05, 0xbd, 0x6b, 0x8e, 0xeb, 0x45, 0x52, 0xfb, 0x61, 0xe5, 0x68, - 0x3f, 0xba, 0xf9, 0x2e, 0xf4, 0x02, 0xf4, 0x91, 0xf5, 0x75, 0x52, 0x8d, 0xc4, 0xac, 0xca, 0x68, - 0x1a, 0x7d, 0x8b, 0xac, 0x94, 0x32, 0xa1, 0xac, 0x31, 0xfe, 0x17, 0x0b, 0x64, 0x74, 0x13, 0x06, - 0x23, 0xb7, 0x41, 0x66, 0x6b, 0x35, 0x61, 0x13, 0x70, 0x0f, 0x21, 0x60, 0xd6, 0x24, 0x01, 0x1c, - 0xd3, 0xb2, 0xbf, 0x58, 0x00, 0x88, 0x43, 0xc1, 0x75, 0xfa, 0xc4, 0xb9, 0x94, 0xb6, 0xf8, 0x6c, - 0x86, 0xb6, 0x18, 0xc5, 0x04, 0x33, 0x54, 0xc5, 0x6a, 0x98, 0x8a, 0x5d, 0x0d, 0x53, 0xcf, 0x41, - 0x86, 0x69, 0x1e, 0x26, 0xe2, 0x50, 0x76, 0x66, 0x24, 0x4f, 0x76, 0x7f, 0xaf, 0x25, 0x81, 0x38, - 0x8d, 0x6f, 0x13, 0x38, 0xa3, 0x22, 0x7a, 0x89, 0xbb, 0x90, 0xb9, 0x12, 0xe8, 0xda, 0xf7, 0x0e, - 0xe3, 0x14, 0xab, 0xc3, 0x0b, 0xb9, 0xea, 0xf0, 0x9f, 0xb2, 0xe0, 0x48, 0xb2, 0x1d, 0xe6, 0x77, - 0xff, 0x79, 0x0b, 0x8e, 0xc6, 0xc9, 0x78, 0xd2, 0x26, 0x08, 0xcf, 0xb7, 0x8d, 0x52, 0x96, 0xd3, - 0xe3, 0x38, 0x6c, 0xcb, 0x4a, 0x16, 0x69, 0x9c, 0xdd, 0xa2, 0xfd, 0xbf, 0x7a, 0x60, 0x2a, 0x2f, - 0xbc, 0x19, 0xf3, 0x34, 0x72, 0xee, 0x54, 0xb6, 0xc8, 0x6d, 0xe1, 0xcf, 0x11, 0x7b, 0x1a, 0xf1, - 0x62, 0x2c, 0xe1, 0xc9, 0xfc, 0x4b, 0x85, 0x2e, 0xf3, 0x2f, 0x6d, 0xc2, 0xc4, 0xed, 0x4d, 0xe2, - 0x5d, 0xf7, 0x42, 0x27, 0x72, 0xc3, 0x75, 0x97, 0x29, 0xd0, 0xf9, 0xba, 0x79, 0x49, 0x7a, 0x5d, - 0xdc, 0x4c, 0x22, 0xec, 0xef, 0x96, 0x4e, 0x19, 0x05, 0x71, 0x97, 0xf9, 0x41, 0x82, 0xd3, 0x44, - 0xd3, 0xe9, 0xab, 0x7a, 0x1e, 0x70, 0xfa, 0xaa, 0x86, 0x2b, 0xcc, 0x6e, 0xa4, 0x1b, 0x09, 0x7b, - 0xb6, 0xae, 0xa8, 0x52, 0xac, 0x61, 0xa0, 0x4f, 0x00, 0xd2, 0xf3, 0x0f, 0x1a, 0xd1, 0x65, 0x9f, - 0xd9, 0xdb, 0x2d, 0xa1, 0xd5, 0x14, 0x74, 0x7f, 0xb7, 0x34, 0x49, 0x4b, 0x97, 0x3d, 0xfa, 0xfc, - 0x8d, 0x43, 0xf2, 0x65, 0x10, 0x42, 0x37, 0x61, 0x9c, 0x96, 0xb2, 0x1d, 0x25, 0x43, 0xd7, 0xf2, - 0x27, 0xeb, 0x53, 0x7b, 0xbb, 0xa5, 0xf1, 0xd5, 0x04, 0x2c, 0x8f, 0x74, 0x8a, 0x48, 0x46, 0x16, - 0xab, 0x81, 0x6e, 0xb3, 0x58, 0xd9, 0x9f, 0xb7, 0xe0, 0x04, 0xbd, 0xe0, 0x6a, 0x57, 0x73, 0xb4, - 0xe8, 0x4e, 0xd3, 0xe5, 0x7a, 0x1a, 0x71, 0xd5, 0x30, 0x59, 0x5d, 0x79, 0x99, 0x6b, 0x69, 0x14, - 0x94, 0x9e, 0xf0, 0x5b, 0xae, 0x57, 0x4b, 0x9e, 0xf0, 0x57, 0x5c, 0xaf, 0x86, 0x19, 0x44, 0x5d, - 0x59, 0xc5, 0xdc, 0x50, 0xf8, 0x5f, 0xa1, 0x7b, 0x95, 0xf6, 0xe5, 0x5b, 0xda, 0x0d, 0xf4, 0x94, - 0xae, 0x53, 0x15, 0xe6, 0x93, 0xb9, 0xfa, 0xd4, 0xcf, 0x59, 0x20, 0xbc, 0xdf, 0xbb, 0xb8, 0x93, - 0xdf, 0x84, 0xe1, 0xed, 0x74, 0x6e, 0xd6, 0x33, 0xf9, 0xe1, 0x00, 0x44, 0x46, 0x56, 0xc5, 0xa2, - 0x1b, 0x79, 0x58, 0x0d, 0x5a, 0x76, 0x0d, 0x04, 0x74, 0x81, 0x30, 0xad, 0x46, 0xe7, 0xde, 0x3c, - 0x0b, 0x50, 0x63, 0xb8, 0x2c, 0x61, 0x7b, 0xc1, 0xe4, 0xb8, 0x16, 0x14, 0x04, 0x6b, 0x58, 0xf6, - 0xcf, 0x15, 0x61, 0x48, 0xe6, 0x02, 0x6d, 0x79, 0xdd, 0xc8, 0x1e, 0x75, 0xc6, 0xa9, 0xd0, 0x91, - 0x71, 0x7a, 0x0b, 0x26, 0x02, 0x52, 0x6d, 0x05, 0xa1, 0xbb, 0x4d, 0x24, 0x58, 0x6c, 0x92, 0x19, - 0x9e, 0x89, 0x21, 0x01, 0xdc, 0x67, 0x21, 0xb2, 0x12, 0x85, 0x4c, 0x69, 0x9c, 0x26, 0x84, 0xce, - 0xc3, 0x20, 0x13, 0xbd, 0x97, 0x63, 0x81, 0xb0, 0x12, 0x7c, 0xad, 0x48, 0x00, 0x8e, 0x71, 0xd8, - 0xe3, 0xa0, 0x75, 0x8b, 0xa1, 0x27, 0x3c, 0xc1, 0x2b, 0xbc, 0x18, 0x4b, 0x38, 0xfa, 0x28, 0x8c, - 0xf3, 0x7a, 0x81, 0xdf, 0x74, 0x36, 0xb8, 0x4a, 0xb0, 0x57, 0x85, 0xd7, 0x19, 0x5f, 0x49, 0xc0, - 0xf6, 0x77, 0x4b, 0x47, 0x92, 0x65, 0xac, 0xdb, 0x29, 0x2a, 0xcc, 0xf2, 0x8f, 0x37, 0x42, 0xef, - 0x8c, 0x94, 0xc1, 0x60, 0x0c, 0xc2, 0x3a, 0x9e, 0xfd, 0x37, 0x16, 0x4c, 0x68, 0x53, 0xd5, 0x75, - 0x32, 0x0c, 0x63, 0x90, 0x0a, 0x5d, 0x0c, 0xd2, 0xc1, 0xa2, 0x3d, 0x64, 0xce, 0x70, 0xcf, 0x7d, - 0x9a, 0x61, 0xfb, 0x53, 0x80, 0xd2, 0x89, 0x66, 0xd1, 0xeb, 0xdc, 0x90, 0xdf, 0x0d, 0x48, 0xad, - 0x9d, 0xc2, 0x5f, 0x8f, 0x9c, 0x23, 0x3d, 0x57, 0x79, 0x2d, 0xac, 0xea, 0xdb, 0x3f, 0xd8, 0x03, - 0xe3, 0xc9, 0x58, 0x1d, 0xe8, 0x32, 0xf4, 0x71, 0x2e, 0x5d, 0x90, 0x6f, 0x63, 0x4f, 0xa6, 0x45, - 0xf8, 0xe0, 0x89, 0x6a, 0x38, 0x77, 0x2f, 0xea, 0xa3, 0xb7, 0x60, 0xa8, 0xe6, 0xdf, 0xf6, 0x6e, - 0x3b, 0x41, 0x6d, 0xb6, 0xbc, 0x2c, 0x4e, 0x88, 0x4c, 0x01, 0xd4, 0x42, 0x8c, 0xa6, 0x47, 0x0d, - 0x61, 0xb6, 0x13, 0x31, 0x08, 0xeb, 0xe4, 0xd0, 0x1a, 0xcb, 0x9d, 0xb4, 0xee, 0x6e, 0xac, 0x38, - 0xcd, 0x76, 0x5e, 0x5d, 0xf3, 0x12, 0x49, 0xa3, 0x3c, 0x22, 0x12, 0x2c, 0x71, 0x00, 0x8e, 0x09, - 0xa1, 0xcf, 0xc0, 0x64, 0x98, 0xa3, 0x12, 0xcb, 0xcb, 0x3b, 0xde, 0x4e, 0x4b, 0xc4, 0x85, 0x29, - 0x59, 0xca, 0xb3, 0xac, 0x66, 0xd0, 0x1d, 0x40, 0x42, 0xf4, 0xbc, 0x16, 0xb4, 0xc2, 0x68, 0xae, - 0xe5, 0xd5, 0xea, 0x32, 0xb7, 0xd2, 0x07, 0xb3, 0xe5, 0x04, 0x49, 0x6c, 0xad, 0x6d, 0x16, 0xbb, - 0x37, 0x8d, 0x81, 0x33, 0xda, 0xb0, 0x3f, 0xd7, 0x03, 0xd3, 0x32, 0xb3, 0x73, 0x86, 0xf7, 0xca, - 0x67, 0xad, 0x84, 0xfb, 0xca, 0x4b, 0xf9, 0x07, 0xfd, 0x03, 0x73, 0x62, 0xf9, 0x42, 0xda, 0x89, - 0xe5, 0x95, 0x03, 0x76, 0xe3, 0xbe, 0xb9, 0xb2, 0xbc, 0x67, 0xfd, 0x4f, 0xf6, 0x8e, 0x80, 0x71, - 0x35, 0x23, 0xcc, 0x03, 0xa3, 0x97, 0xa5, 0xea, 0x28, 0xe7, 0xf9, 0x7f, 0x59, 0xe0, 0x18, 0x97, - 0xfd, 0xb0, 0x0c, 0x9f, 0xce, 0xce, 0x59, 0x45, 0x87, 0xd2, 0x24, 0x8d, 0x66, 0xb4, 0xb3, 0xe0, - 0x06, 0xa2, 0xc7, 0x99, 0x34, 0x17, 0x05, 0x4e, 0x9a, 0xa6, 0x84, 0x60, 0x45, 0x07, 0x6d, 0xc3, - 0xc4, 0x06, 0x8b, 0xf8, 0xa4, 0x25, 0x59, 0x16, 0xe7, 0x42, 0xe6, 0xbe, 0xbd, 0x34, 0xbf, 0x98, - 0x9f, 0x91, 0x99, 0x3f, 0xfe, 0x52, 0x28, 0x38, 0xdd, 0x04, 0xdd, 0x1a, 0x47, 0x9c, 0xdb, 0xe1, - 0x62, 0xdd, 0x09, 0x23, 0xb7, 0x3a, 0x57, 0xf7, 0xab, 0x5b, 0x95, 0xc8, 0x0f, 0x64, 0x26, 0xc6, - 0xcc, 0xb7, 0xd7, 0xec, 0xcd, 0x4a, 0x0a, 0xdf, 0x68, 0x7e, 0x6a, 0x6f, 0xb7, 0x74, 0x24, 0x0b, - 0x0b, 0x67, 0xb6, 0x85, 0x56, 0xa1, 0x7f, 0xc3, 0x8d, 0x30, 0x69, 0xfa, 0xe2, 0xb4, 0xc8, 0x3c, - 0x0a, 0x2f, 0x71, 0x14, 0xa3, 0x25, 0x16, 0x91, 0x4a, 0x00, 0xb0, 0x24, 0x82, 0x5e, 0x57, 0x97, - 0x40, 0x5f, 0xbe, 0x00, 0x36, 0x6d, 0x7b, 0x97, 0x79, 0x0d, 0xbc, 0x0a, 0x45, 0x6f, 0x3d, 0x6c, - 0x17, 0x8b, 0x67, 0x75, 0xc9, 0x90, 0x9f, 0xcd, 0xf5, 0xd3, 0xa7, 0xf1, 0xea, 0x52, 0x05, 0xd3, - 0x8a, 0xcc, 0xed, 0x35, 0xac, 0x86, 0xae, 0xc8, 0xea, 0x94, 0xe9, 0x05, 0xbc, 0x5c, 0x99, 0xaf, - 0x2c, 0x1b, 0x34, 0x58, 0x54, 0x43, 0x56, 0x8c, 0x79, 0x75, 0x74, 0x03, 0x06, 0x37, 0xf8, 0xc1, - 0xb7, 0x1e, 0x8a, 0xec, 0xee, 0x99, 0x97, 0xd1, 0x25, 0x89, 0x64, 0xd0, 0x63, 0x57, 0x86, 0x02, - 0xe1, 0x98, 0x14, 0xfa, 0x9c, 0x05, 0x47, 0x93, 0xe9, 0xf1, 0x99, 0xb3, 0x9a, 0x30, 0x53, 0xcb, - 0x74, 0x00, 0x28, 0x67, 0x55, 0x30, 0x1a, 0x64, 0xea, 0x97, 0x4c, 0x34, 0x9c, 0xdd, 0x1c, 0x1d, - 0xe8, 0xe0, 0x56, 0xad, 0x5d, 0x22, 0xa0, 0x44, 0x60, 0x22, 0x3e, 0xd0, 0x78, 0x6e, 0x01, 0xd3, - 0x8a, 0x68, 0x0d, 0x60, 0xbd, 0x4e, 0x44, 0xc4, 0x47, 0x61, 0x14, 0x95, 0x79, 0xfb, 0x2f, 0x29, - 0x2c, 0x41, 0x87, 0xbd, 0x44, 0xe3, 0x52, 0xac, 0xd1, 0xa1, 0x4b, 0xa9, 0xea, 0x7a, 0x35, 0x12, - 0x30, 0xe5, 0x56, 0xce, 0x52, 0x9a, 0x67, 0x18, 0xe9, 0xa5, 0xc4, 0xcb, 0xb1, 0xa0, 0xc0, 0x68, - 0x91, 0xe6, 0xe6, 0x7a, 0xd8, 0x2e, 0xe5, 0xc4, 0x3c, 0x69, 0x6e, 0x26, 0x16, 0x14, 0xa7, 0xc5, - 0xca, 0xb1, 0xa0, 0x40, 0xb7, 0xcc, 0x3a, 0xdd, 0x40, 0x24, 0x98, 0x1a, 0xcb, 0xdf, 0x32, 0x4b, - 0x1c, 0x25, 0xbd, 0x65, 0x04, 0x00, 0x4b, 0x22, 0xe8, 0x93, 0x26, 0xb7, 0x33, 0xce, 0x68, 0x3e, - 0xd5, 0x81, 0xdb, 0x31, 0xe8, 0xb6, 0xe7, 0x77, 0x5e, 0x82, 0xc2, 0x7a, 0x95, 0x29, 0xc5, 0x72, - 0x74, 0x06, 0x4b, 0xf3, 0x06, 0x35, 0x16, 0xc2, 0x7d, 0x69, 0x1e, 0x17, 0xd6, 0xab, 0x74, 0xe9, - 0x3b, 0x77, 0x5b, 0x01, 0x59, 0x72, 0xeb, 0x44, 0xa4, 0x9f, 0xc8, 0x5c, 0xfa, 0xb3, 0x12, 0x29, - 0xbd, 0xf4, 0x15, 0x08, 0xc7, 0xa4, 0x28, 0xdd, 0x98, 0x07, 0x9b, 0xcc, 0xa7, 0xab, 0x58, 0xad, - 0x34, 0xdd, 0x4c, 0x2e, 0x6c, 0x0b, 0x46, 0xb6, 0xc3, 0xe6, 0x26, 0x91, 0xa7, 0x22, 0x53, 0xd7, - 0xe5, 0x44, 0xaa, 0xb8, 0x21, 0x10, 0xdd, 0x20, 0x6a, 0x39, 0xf5, 0xd4, 0x41, 0xce, 0x44, 0x2b, - 0x37, 0x74, 0x62, 0xd8, 0xa4, 0x4d, 0x17, 0xc2, 0xdb, 0x3c, 0x9c, 0x1c, 0x53, 0xdc, 0xe5, 0x2c, - 0x84, 0x8c, 0x88, 0x73, 0x7c, 0x21, 0x08, 0x00, 0x96, 0x44, 0xd4, 0x60, 0xb3, 0x0b, 0xe8, 0x58, - 0x87, 0xc1, 0x4e, 0xf5, 0x37, 0x1e, 0x6c, 0x76, 0xe1, 0xc4, 0xa4, 0xd8, 0x45, 0xd3, 0xdc, 0xf4, - 0x23, 0xdf, 0x4b, 0x5c, 0x72, 0xc7, 0xf3, 0x2f, 0x9a, 0x72, 0x06, 0x7e, 0xfa, 0xa2, 0xc9, 0xc2, - 0xc2, 0x99, 0x6d, 0xd1, 0x8f, 0x6b, 0xca, 0xc8, 0x80, 0x22, 0x45, 0xc6, 0x13, 0x39, 0x81, 0x35, - 0xd3, 0xe1, 0x03, 0xf9, 0xc7, 0x29, 0x10, 0x8e, 0x49, 0xa1, 0x1a, 0x8c, 0x36, 0x8d, 0x88, 0xb3, - 0x2c, 0xd5, 0x47, 0x0e, 0x5f, 0x90, 0x15, 0x9b, 0x96, 0x4b, 0x88, 0x4c, 0x08, 0x4e, 0xd0, 0x64, - 0x96, 0x7b, 0xdc, 0xd5, 0x8f, 0x65, 0x02, 0xc9, 0x99, 0xea, 0x0c, 0x6f, 0x40, 0x3e, 0xd5, 0x02, - 0x80, 0x25, 0x11, 0x3a, 0x1a, 0xc2, 0x41, 0xcd, 0x0f, 0x59, 0x42, 0x9d, 0x3c, 0x05, 0x7b, 0x96, - 0x9a, 0x48, 0x86, 0x59, 0x17, 0x20, 0x1c, 0x93, 0xa2, 0x27, 0x39, 0xbd, 0xf0, 0x4e, 0xe6, 0x9f, - 0xe4, 0xc9, 0xeb, 0x8e, 0x9d, 0xe4, 0xf4, 0xb2, 0x2b, 0x8a, 0xab, 0x4e, 0x45, 0x05, 0x67, 0xc9, - 0x40, 0x72, 0xfa, 0xa5, 0xc2, 0x8a, 0xa7, 0xfb, 0xa5, 0x40, 0x38, 0x26, 0xc5, 0xae, 0x62, 0x16, - 0x9a, 0xee, 0x74, 0x9b, 0xab, 0x98, 0x22, 0x64, 0x5c, 0xc5, 0x5a, 0xe8, 0x3a, 0xfb, 0x07, 0x0b, - 0x70, 0xba, 0xfd, 0xbe, 0x8d, 0x75, 0x68, 0xe5, 0xd8, 0x66, 0x29, 0xa1, 0x43, 0xe3, 0x12, 0x9d, - 0x18, 0xab, 0xeb, 0x80, 0xc3, 0x97, 0x60, 0x42, 0xb9, 0x23, 0xd6, 0xdd, 0xea, 0x8e, 0x96, 0x01, - 0x54, 0x85, 0xe6, 0xa9, 0x24, 0x11, 0x70, 0xba, 0x0e, 0x9a, 0x85, 0x31, 0xa3, 0x70, 0x79, 0x41, - 0x3c, 0xff, 0xe3, 0x34, 0x16, 0x26, 0x18, 0x27, 0xf1, 0xed, 0x9f, 0xb7, 0xe0, 0x78, 0x4e, 0x42, - 0xf8, 0xae, 0xe3, 0xe9, 0xae, 0xc3, 0x58, 0xd3, 0xac, 0xda, 0x21, 0x04, 0xb8, 0x91, 0x76, 0x5e, - 0xf5, 0x35, 0x01, 0xc0, 0x49, 0xa2, 0xf6, 0xcf, 0x16, 0xe0, 0x54, 0x5b, 0xfb, 0x7a, 0x84, 0xe1, - 0xd8, 0x46, 0x23, 0x74, 0xe6, 0x03, 0x52, 0x23, 0x5e, 0xe4, 0x3a, 0xf5, 0x4a, 0x93, 0x54, 0x35, - 0x2d, 0x28, 0x33, 0x54, 0xbf, 0xb4, 0x52, 0x99, 0x4d, 0x63, 0xe0, 0x9c, 0x9a, 0x68, 0x09, 0x50, - 0x1a, 0x22, 0x66, 0x98, 0x3d, 0x71, 0xd3, 0xf4, 0x70, 0x46, 0x0d, 0xf4, 0x22, 0x8c, 0x28, 0xbb, - 0x7d, 0x6d, 0xc6, 0xd9, 0x05, 0x81, 0x75, 0x00, 0x36, 0xf1, 0xd0, 0x05, 0x9e, 0xdf, 0x48, 0x64, - 0xc2, 0x12, 0x2a, 0xd3, 0x31, 0x99, 0xbc, 0x48, 0x14, 0x63, 0x1d, 0x67, 0xee, 0xe2, 0xef, 0x7d, - 0xf3, 0xf4, 0x07, 0xfe, 0xf0, 0x9b, 0xa7, 0x3f, 0xf0, 0xc7, 0xdf, 0x3c, 0xfd, 0x81, 0xef, 0xd9, - 0x3b, 0x6d, 0xfd, 0xde, 0xde, 0x69, 0xeb, 0x0f, 0xf7, 0x4e, 0x5b, 0x7f, 0xbc, 0x77, 0xda, 0xfa, - 0xaf, 0x7b, 0xa7, 0xad, 0x2f, 0xfe, 0xd9, 0xe9, 0x0f, 0xbc, 0x89, 0xe2, 0x08, 0xd5, 0xe7, 0xe9, - 0xec, 0x9c, 0xdf, 0xbe, 0xf0, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x66, 0x0b, 0xa2, 0x01, 0x4c, - 0x24, 0x01, 0x00, + // 16114 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x69, 0x90, 0x64, 0xd9, + 0x59, 0x28, 0xa6, 0x9b, 0x59, 0xeb, 0x57, 0xfb, 0xa9, 0x5e, 0xaa, 0x6b, 0xba, 0x3b, 0x7b, 0xee, + 0xcc, 0xf4, 0xf4, 0x6c, 0xd5, 0xea, 0x59, 0x34, 0xad, 0x99, 0xd1, 0x30, 0xb5, 0x76, 0xd7, 0x74, + 0x57, 0x75, 0xce, 0xc9, 0xaa, 0x6e, 0x69, 0x34, 0x12, 0xba, 0x9d, 0x79, 0xaa, 0xea, 0xaa, 0x32, + 0xef, 0xcd, 0xb9, 0xf7, 0x66, 0x75, 0x57, 0x5b, 0x04, 0x20, 0x8c, 0x40, 0x02, 0x47, 0x28, 0x08, + 0x6c, 0x1c, 0x82, 0xe0, 0x07, 0x60, 0x16, 0xcb, 0x60, 0x64, 0x61, 0xc0, 0x88, 0xcd, 0x36, 0x8e, + 0x00, 0xff, 0xc0, 0x98, 0x08, 0x4b, 0x84, 0x09, 0x17, 0x56, 0xe1, 0x08, 0x82, 0x1f, 0x06, 0x82, + 0xf7, 0x7e, 0xbc, 0x57, 0xc1, 0x7b, 0xbc, 0x38, 0xeb, 0x3d, 0xe7, 0x2e, 0x99, 0x59, 0x3d, 0xdd, + 0xa5, 0x91, 0x62, 0xfe, 0x65, 0x9e, 0xef, 0x3b, 0xdf, 0x39, 0xf7, 0xac, 0xdf, 0xf9, 0x56, 0xb0, + 0xb7, 0x2f, 0x87, 0x33, 0xae, 0x7f, 0xd1, 0x69, 0xba, 0x17, 0xab, 0x7e, 0x40, 0x2e, 0xee, 0x5c, + 0xba, 0xb8, 0x49, 0x3c, 0x12, 0x38, 0x11, 0xa9, 0xcd, 0x34, 0x03, 0x3f, 0xf2, 0x11, 0xe2, 0x38, + 0x33, 0x4e, 0xd3, 0x9d, 0xa1, 0x38, 0x33, 0x3b, 0x97, 0xa6, 0x9f, 0xdb, 0x74, 0xa3, 0xad, 0xd6, + 0xed, 0x99, 0xaa, 0xdf, 0xb8, 0xb8, 0xe9, 0x6f, 0xfa, 0x17, 0x19, 0xea, 0xed, 0xd6, 0x06, 0xfb, + 0xc7, 0xfe, 0xb0, 0x5f, 0x9c, 0xc4, 0xf4, 0x8b, 0x71, 0x33, 0x0d, 0xa7, 0xba, 0xe5, 0x7a, 0x24, + 0xd8, 0xbd, 0xd8, 0xdc, 0xde, 0x64, 0xed, 0x06, 0x24, 0xf4, 0x5b, 0x41, 0x95, 0x24, 0x1b, 0x6e, + 0x5b, 0x2b, 0xbc, 0xd8, 0x20, 0x91, 0x93, 0xd1, 0xdd, 0xe9, 0x8b, 0x79, 0xb5, 0x82, 0x96, 0x17, + 0xb9, 0x8d, 0x74, 0x33, 0x1f, 0xe9, 0x54, 0x21, 0xac, 0x6e, 0x91, 0x86, 0x93, 0xaa, 0xf7, 0x42, + 0x5e, 0xbd, 0x56, 0xe4, 0xd6, 0x2f, 0xba, 0x5e, 0x14, 0x46, 0x41, 0xb2, 0x92, 0xfd, 0x2d, 0x0b, + 0xce, 0xcd, 0xde, 0xaa, 0x2c, 0xd6, 0x9d, 0x30, 0x72, 0xab, 0x73, 0x75, 0xbf, 0xba, 0x5d, 0x89, + 0xfc, 0x80, 0xdc, 0xf4, 0xeb, 0xad, 0x06, 0xa9, 0xb0, 0x81, 0x40, 0xcf, 0xc2, 0xc0, 0x0e, 0xfb, + 0xbf, 0xbc, 0x30, 0x65, 0x9d, 0xb3, 0x2e, 0x0c, 0xce, 0x8d, 0xff, 0xe9, 0x5e, 0xe9, 0x43, 0xfb, + 0x7b, 0xa5, 0x81, 0x9b, 0xa2, 0x1c, 0x2b, 0x0c, 0x74, 0x1e, 0xfa, 0x36, 0xc2, 0xb5, 0xdd, 0x26, + 0x99, 0x2a, 0x30, 0xdc, 0x51, 0x81, 0xdb, 0xb7, 0x54, 0xa1, 0xa5, 0x58, 0x40, 0xd1, 0x45, 0x18, + 0x6c, 0x3a, 0x41, 0xe4, 0x46, 0xae, 0xef, 0x4d, 0x15, 0xcf, 0x59, 0x17, 0x7a, 0xe7, 0x26, 0x04, + 0xea, 0x60, 0x59, 0x02, 0x70, 0x8c, 0x43, 0xbb, 0x11, 0x10, 0xa7, 0x76, 0xc3, 0xab, 0xef, 0x4e, + 0xf5, 0x9c, 0xb3, 0x2e, 0x0c, 0xc4, 0xdd, 0xc0, 0xa2, 0x1c, 0x2b, 0x0c, 0xfb, 0x2b, 0x05, 0x18, + 0x98, 0xdd, 0xd8, 0x70, 0x3d, 0x37, 0xda, 0x45, 0x37, 0x61, 0xd8, 0xf3, 0x6b, 0x44, 0xfe, 0x67, + 0x5f, 0x31, 0xf4, 0xfc, 0xb9, 0x99, 0xf4, 0x52, 0x9a, 0x59, 0xd5, 0xf0, 0xe6, 0xc6, 0xf7, 0xf7, + 0x4a, 0xc3, 0x7a, 0x09, 0x36, 0xe8, 0x20, 0x0c, 0x43, 0x4d, 0xbf, 0xa6, 0xc8, 0x16, 0x18, 0xd9, + 0x52, 0x16, 0xd9, 0x72, 0x8c, 0x36, 0x37, 0xb6, 0xbf, 0x57, 0x1a, 0xd2, 0x0a, 0xb0, 0x4e, 0x04, + 0xdd, 0x86, 0x31, 0xfa, 0xd7, 0x8b, 0x5c, 0x45, 0xb7, 0xc8, 0xe8, 0x3e, 0x96, 0x47, 0x57, 0x43, + 0x9d, 0x9b, 0xdc, 0xdf, 0x2b, 0x8d, 0x25, 0x0a, 0x71, 0x92, 0xa0, 0xfd, 0x93, 0x16, 0x8c, 0xcd, + 0x36, 0x9b, 0xb3, 0x41, 0xc3, 0x0f, 0xca, 0x81, 0xbf, 0xe1, 0xd6, 0x09, 0x7a, 0x19, 0x7a, 0x22, + 0x3a, 0x6b, 0x7c, 0x86, 0x1f, 0x13, 0x43, 0xdb, 0x43, 0xe7, 0xea, 0x60, 0xaf, 0x34, 0x99, 0x40, + 0x67, 0x53, 0xc9, 0x2a, 0xa0, 0x37, 0x60, 0xbc, 0xee, 0x57, 0x9d, 0xfa, 0x96, 0x1f, 0x46, 0x02, + 0x2a, 0xa6, 0xfe, 0xd8, 0xfe, 0x5e, 0x69, 0xfc, 0x7a, 0x02, 0x86, 0x53, 0xd8, 0xf6, 0x3d, 0x18, + 0x9d, 0x8d, 0x22, 0xa7, 0xba, 0x45, 0x6a, 0x7c, 0x41, 0xa1, 0x17, 0xa1, 0xc7, 0x73, 0x1a, 0xb2, + 0x33, 0xe7, 0x64, 0x67, 0x56, 0x9d, 0x06, 0xed, 0xcc, 0xf8, 0xba, 0xe7, 0xbe, 0xdb, 0x12, 0x8b, + 0x94, 0x96, 0x61, 0x86, 0x8d, 0x9e, 0x07, 0xa8, 0x91, 0x1d, 0xb7, 0x4a, 0xca, 0x4e, 0xb4, 0x25, + 0xfa, 0x80, 0x44, 0x5d, 0x58, 0x50, 0x10, 0xac, 0x61, 0xd9, 0x77, 0x61, 0x70, 0x76, 0xc7, 0x77, + 0x6b, 0x65, 0xbf, 0x16, 0xa2, 0x6d, 0x18, 0x6b, 0x06, 0x64, 0x83, 0x04, 0xaa, 0x68, 0xca, 0x3a, + 0x57, 0xbc, 0x30, 0xf4, 0xfc, 0x85, 0xcc, 0xb1, 0x37, 0x51, 0x17, 0xbd, 0x28, 0xd8, 0x9d, 0x3b, + 0x29, 0xda, 0x1b, 0x4b, 0x40, 0x71, 0x92, 0xb2, 0xfd, 0x27, 0x05, 0x38, 0x3e, 0x7b, 0xaf, 0x15, + 0x90, 0x05, 0x37, 0xdc, 0x4e, 0x6e, 0xb8, 0x9a, 0x1b, 0x6e, 0xaf, 0xc6, 0x23, 0xa0, 0x56, 0xfa, + 0x82, 0x28, 0xc7, 0x0a, 0x03, 0x3d, 0x07, 0xfd, 0xf4, 0xf7, 0x3a, 0x5e, 0x16, 0x9f, 0x3c, 0x29, + 0x90, 0x87, 0x16, 0x9c, 0xc8, 0x59, 0xe0, 0x20, 0x2c, 0x71, 0xd0, 0x0a, 0x0c, 0x55, 0xd9, 0xf9, + 0xb0, 0xb9, 0xe2, 0xd7, 0x08, 0x5b, 0x5b, 0x83, 0x73, 0xcf, 0x50, 0xf4, 0xf9, 0xb8, 0xf8, 0x60, + 0xaf, 0x34, 0xc5, 0xfb, 0x26, 0x48, 0x68, 0x30, 0xac, 0xd7, 0x47, 0xb6, 0xda, 0xee, 0x3d, 0x8c, + 0x12, 0x64, 0x6c, 0xf5, 0x0b, 0xda, 0xce, 0xed, 0x65, 0x3b, 0x77, 0x38, 0x7b, 0xd7, 0xa2, 0x4b, + 0xd0, 0xb3, 0xed, 0x7a, 0xb5, 0xa9, 0x3e, 0x46, 0xeb, 0x0c, 0x9d, 0xf3, 0x6b, 0xae, 0x57, 0x3b, + 0xd8, 0x2b, 0x4d, 0x18, 0xdd, 0xa1, 0x85, 0x98, 0xa1, 0xda, 0xff, 0xc6, 0x82, 0x12, 0x83, 0x2d, + 0xb9, 0x75, 0x52, 0x26, 0x41, 0xe8, 0x86, 0x11, 0xf1, 0x22, 0x63, 0x40, 0x9f, 0x07, 0x08, 0x49, + 0x35, 0x20, 0x91, 0x36, 0xa4, 0x6a, 0x61, 0x54, 0x14, 0x04, 0x6b, 0x58, 0xf4, 0x7c, 0x0a, 0xb7, + 0x9c, 0x80, 0xad, 0x2f, 0x31, 0xb0, 0xea, 0x7c, 0xaa, 0x48, 0x00, 0x8e, 0x71, 0x8c, 0xf3, 0xa9, + 0xd8, 0xe9, 0x7c, 0x42, 0x1f, 0x83, 0xb1, 0xb8, 0xb1, 0xb0, 0xe9, 0x54, 0xe5, 0x00, 0xb2, 0x1d, + 0x5c, 0x31, 0x41, 0x38, 0x89, 0x6b, 0xff, 0xb7, 0x96, 0x58, 0x3c, 0xf4, 0xab, 0xdf, 0xe7, 0xdf, + 0x6a, 0xff, 0xae, 0x05, 0xfd, 0x73, 0xae, 0x57, 0x73, 0xbd, 0x4d, 0xf4, 0x19, 0x18, 0xa0, 0x57, + 0x65, 0xcd, 0x89, 0x1c, 0x71, 0x0c, 0x7f, 0x58, 0xdb, 0x5b, 0xea, 0xe6, 0x9a, 0x69, 0x6e, 0x6f, + 0xd2, 0x82, 0x70, 0x86, 0x62, 0xd3, 0xdd, 0x76, 0xe3, 0xf6, 0x67, 0x49, 0x35, 0x5a, 0x21, 0x91, + 0x13, 0x7f, 0x4e, 0x5c, 0x86, 0x15, 0x55, 0x74, 0x0d, 0xfa, 0x22, 0x27, 0xd8, 0x24, 0x91, 0x38, + 0x8f, 0x33, 0xcf, 0x4d, 0x5e, 0x13, 0xd3, 0x1d, 0x49, 0xbc, 0x2a, 0x89, 0x6f, 0xa9, 0x35, 0x56, + 0x15, 0x0b, 0x12, 0xf6, 0x7f, 0xe8, 0x87, 0x53, 0xf3, 0x95, 0xe5, 0x9c, 0x75, 0x75, 0x1e, 0xfa, + 0x6a, 0x81, 0xbb, 0x43, 0x02, 0x31, 0xce, 0x8a, 0xca, 0x02, 0x2b, 0xc5, 0x02, 0x8a, 0x2e, 0xc3, + 0x30, 0xbf, 0x1f, 0xaf, 0x3a, 0x5e, 0x2d, 0x3e, 0x1e, 0x05, 0xf6, 0xf0, 0x4d, 0x0d, 0x86, 0x0d, + 0xcc, 0x43, 0x2e, 0xaa, 0xf3, 0x89, 0xcd, 0x98, 0x77, 0xf7, 0x7e, 0xd1, 0x82, 0x71, 0xde, 0xcc, + 0x6c, 0x14, 0x05, 0xee, 0xed, 0x56, 0x44, 0xc2, 0xa9, 0x5e, 0x76, 0xd2, 0xcd, 0x67, 0x8d, 0x56, + 0xee, 0x08, 0xcc, 0xdc, 0x4c, 0x50, 0xe1, 0x87, 0xe0, 0x94, 0x68, 0x77, 0x3c, 0x09, 0xc6, 0xa9, + 0x66, 0xd1, 0x8f, 0x58, 0x30, 0x5d, 0xf5, 0xbd, 0x28, 0xf0, 0xeb, 0x75, 0x12, 0x94, 0x5b, 0xb7, + 0xeb, 0x6e, 0xb8, 0xc5, 0xd7, 0x29, 0x26, 0x1b, 0xec, 0x24, 0xc8, 0x99, 0x43, 0x85, 0x24, 0xe6, + 0xf0, 0xec, 0xfe, 0x5e, 0x69, 0x7a, 0x3e, 0x97, 0x14, 0x6e, 0xd3, 0x0c, 0xda, 0x06, 0x44, 0x6f, + 0xf6, 0x4a, 0xe4, 0x6c, 0x92, 0xb8, 0xf1, 0xfe, 0xee, 0x1b, 0x3f, 0xb1, 0xbf, 0x57, 0x42, 0xab, + 0x29, 0x12, 0x38, 0x83, 0x2c, 0x7a, 0x17, 0x8e, 0xd1, 0xd2, 0xd4, 0xb7, 0x0e, 0x74, 0xdf, 0xdc, + 0xd4, 0xfe, 0x5e, 0xe9, 0xd8, 0x6a, 0x06, 0x11, 0x9c, 0x49, 0x1a, 0xfd, 0x90, 0x05, 0xa7, 0xe2, + 0xcf, 0x5f, 0xbc, 0xdb, 0x74, 0xbc, 0x5a, 0xdc, 0xf0, 0x60, 0xf7, 0x0d, 0xd3, 0x33, 0xf9, 0xd4, + 0x7c, 0x1e, 0x25, 0x9c, 0xdf, 0x08, 0xf2, 0x60, 0x92, 0x76, 0x2d, 0xd9, 0x36, 0x74, 0xdf, 0xf6, + 0xc9, 0xfd, 0xbd, 0xd2, 0xe4, 0x6a, 0x9a, 0x06, 0xce, 0x22, 0x3c, 0x3d, 0x0f, 0xc7, 0x33, 0x57, + 0x27, 0x1a, 0x87, 0xe2, 0x36, 0xe1, 0x4c, 0xe0, 0x20, 0xa6, 0x3f, 0xd1, 0x31, 0xe8, 0xdd, 0x71, + 0xea, 0x2d, 0xb1, 0x31, 0x31, 0xff, 0xf3, 0x4a, 0xe1, 0xb2, 0x65, 0xff, 0x6f, 0x45, 0x18, 0x9b, + 0xaf, 0x2c, 0xdf, 0xd7, 0xae, 0xd7, 0xaf, 0xbd, 0x42, 0xdb, 0x6b, 0x2f, 0xbe, 0x44, 0x8b, 0xb9, + 0x97, 0xe8, 0x0f, 0x66, 0x6c, 0xd9, 0x1e, 0xb6, 0x65, 0x3f, 0x9a, 0xb3, 0x65, 0x1f, 0xf0, 0x46, + 0xdd, 0xc9, 0x59, 0xb5, 0xbd, 0x6c, 0x02, 0x33, 0x39, 0x24, 0xc6, 0xfb, 0x25, 0x8f, 0xda, 0x43, + 0x2e, 0xdd, 0x07, 0x33, 0x8f, 0x55, 0x18, 0x9e, 0x77, 0x9a, 0xce, 0x6d, 0xb7, 0xee, 0x46, 0x2e, + 0x09, 0xd1, 0x93, 0x50, 0x74, 0x6a, 0x35, 0xc6, 0xdd, 0x0d, 0xce, 0x1d, 0xdf, 0xdf, 0x2b, 0x15, + 0x67, 0x6b, 0x94, 0xcd, 0x00, 0x85, 0xb5, 0x8b, 0x29, 0x06, 0x7a, 0x1a, 0x7a, 0x6a, 0x81, 0xdf, + 0x9c, 0x2a, 0x30, 0x4c, 0xba, 0xcb, 0x7b, 0x16, 0x02, 0xbf, 0x99, 0x40, 0x65, 0x38, 0xf6, 0x1f, + 0x17, 0xe0, 0xf4, 0x3c, 0x69, 0x6e, 0x2d, 0x55, 0x72, 0xee, 0x8b, 0x0b, 0x30, 0xd0, 0xf0, 0x3d, + 0x37, 0xf2, 0x83, 0x50, 0x34, 0xcd, 0x56, 0xc4, 0x8a, 0x28, 0xc3, 0x0a, 0x8a, 0xce, 0x41, 0x4f, + 0x33, 0x66, 0x62, 0x87, 0x25, 0x03, 0xcc, 0xd8, 0x57, 0x06, 0xa1, 0x18, 0xad, 0x90, 0x04, 0x62, + 0xc5, 0x28, 0x8c, 0xf5, 0x90, 0x04, 0x98, 0x41, 0x62, 0x4e, 0x80, 0xf2, 0x08, 0xe2, 0x46, 0x48, + 0x70, 0x02, 0x14, 0x82, 0x35, 0x2c, 0x54, 0x86, 0xc1, 0x30, 0x31, 0xb3, 0x5d, 0x6d, 0xcd, 0x11, + 0xc6, 0x2a, 0xa8, 0x99, 0x8c, 0x89, 0x18, 0x37, 0x58, 0x5f, 0x47, 0x56, 0xe1, 0x1b, 0x05, 0x40, + 0x7c, 0x08, 0xbf, 0xcb, 0x06, 0x6e, 0x3d, 0x3d, 0x70, 0xdd, 0x6f, 0x89, 0x07, 0x35, 0x7a, 0xff, + 0xd6, 0x82, 0xd3, 0xf3, 0xae, 0x57, 0x23, 0x41, 0xce, 0x02, 0x7c, 0x38, 0x4f, 0xf9, 0xc3, 0x31, + 0x29, 0xc6, 0x12, 0xeb, 0x79, 0x00, 0x4b, 0xcc, 0xfe, 0x47, 0x0b, 0x10, 0xff, 0xec, 0xf7, 0xdd, + 0xc7, 0xae, 0xa7, 0x3f, 0xf6, 0x01, 0x2c, 0x0b, 0xfb, 0x3a, 0x8c, 0xce, 0xd7, 0x5d, 0xe2, 0x45, + 0xcb, 0xe5, 0x79, 0xdf, 0xdb, 0x70, 0x37, 0xd1, 0x2b, 0x30, 0x1a, 0xb9, 0x0d, 0xe2, 0xb7, 0xa2, + 0x0a, 0xa9, 0xfa, 0x1e, 0x7b, 0xb9, 0x5a, 0x17, 0x7a, 0xe7, 0xd0, 0xfe, 0x5e, 0x69, 0x74, 0xcd, + 0x80, 0xe0, 0x04, 0xa6, 0xfd, 0xcb, 0xf4, 0xdc, 0xaa, 0xb7, 0xc2, 0x88, 0x04, 0x6b, 0x41, 0x2b, + 0x8c, 0xe6, 0x5a, 0x94, 0xf7, 0x2c, 0x07, 0x3e, 0xed, 0x8e, 0xeb, 0x7b, 0xe8, 0xb4, 0xf1, 0x1c, + 0x1f, 0x90, 0x4f, 0x71, 0xf1, 0xec, 0x9e, 0x01, 0x08, 0xdd, 0x4d, 0x8f, 0x04, 0xda, 0xf3, 0x61, + 0x94, 0x6d, 0x15, 0x55, 0x8a, 0x35, 0x0c, 0x54, 0x87, 0x91, 0xba, 0x73, 0x9b, 0xd4, 0x2b, 0xa4, + 0x4e, 0xaa, 0x91, 0x1f, 0x08, 0xf9, 0xc6, 0x0b, 0xdd, 0xbd, 0x03, 0xae, 0xeb, 0x55, 0xe7, 0x26, + 0xf6, 0xf7, 0x4a, 0x23, 0x46, 0x11, 0x36, 0x89, 0xd3, 0xa3, 0xc3, 0x6f, 0xd2, 0xaf, 0x70, 0xea, + 0xfa, 0xe3, 0xf3, 0x86, 0x28, 0xc3, 0x0a, 0xaa, 0x8e, 0x8e, 0x9e, 0xbc, 0xa3, 0xc3, 0xfe, 0x6b, + 0xba, 0xd0, 0xfc, 0x46, 0xd3, 0xf7, 0x88, 0x17, 0xcd, 0xfb, 0x5e, 0x8d, 0x4b, 0xa6, 0x5e, 0x31, + 0x44, 0x27, 0xe7, 0x13, 0xa2, 0x93, 0x13, 0xe9, 0x1a, 0x9a, 0xf4, 0xe4, 0xa3, 0xd0, 0x17, 0x46, + 0x4e, 0xd4, 0x0a, 0xc5, 0xc0, 0x3d, 0x2a, 0x97, 0x5d, 0x85, 0x95, 0x1e, 0xec, 0x95, 0xc6, 0x54, + 0x35, 0x5e, 0x84, 0x45, 0x05, 0xf4, 0x14, 0xf4, 0x37, 0x48, 0x18, 0x3a, 0x9b, 0x92, 0x6d, 0x18, + 0x13, 0x75, 0xfb, 0x57, 0x78, 0x31, 0x96, 0x70, 0xf4, 0x18, 0xf4, 0x92, 0x20, 0xf0, 0x03, 0xf1, + 0x6d, 0x23, 0x02, 0xb1, 0x77, 0x91, 0x16, 0x62, 0x0e, 0xb3, 0xff, 0x0f, 0x0b, 0xc6, 0x54, 0x5f, + 0x79, 0x5b, 0x47, 0xf0, 0x5c, 0x7b, 0x1b, 0xa0, 0x2a, 0x3f, 0x30, 0x64, 0xd7, 0xec, 0xd0, 0xf3, + 0xe7, 0x33, 0x39, 0x9a, 0xd4, 0x30, 0xc6, 0x94, 0x55, 0x51, 0x88, 0x35, 0x6a, 0xf6, 0x1f, 0x58, + 0x30, 0x99, 0xf8, 0xa2, 0xeb, 0x6e, 0x18, 0xa1, 0x77, 0x52, 0x5f, 0x35, 0xd3, 0xe5, 0xe2, 0x73, + 0x43, 0xfe, 0x4d, 0x6a, 0xcf, 0xcb, 0x12, 0xed, 0x8b, 0xae, 0x42, 0xaf, 0x1b, 0x91, 0x86, 0xfc, + 0x98, 0xc7, 0xda, 0x7e, 0x0c, 0xef, 0x55, 0x3c, 0x23, 0xcb, 0xb4, 0x26, 0xe6, 0x04, 0xec, 0x3f, + 0x2e, 0xc2, 0x20, 0xdf, 0xdf, 0x2b, 0x4e, 0xf3, 0x08, 0xe6, 0xe2, 0x19, 0x18, 0x74, 0x1b, 0x8d, + 0x56, 0xe4, 0xdc, 0x16, 0xf7, 0xde, 0x00, 0x3f, 0x83, 0x96, 0x65, 0x21, 0x8e, 0xe1, 0x68, 0x19, + 0x7a, 0x58, 0x57, 0xf8, 0x57, 0x3e, 0x99, 0xfd, 0x95, 0xa2, 0xef, 0x33, 0x0b, 0x4e, 0xe4, 0x70, + 0x96, 0x53, 0xed, 0x2b, 0x5a, 0x84, 0x19, 0x09, 0xe4, 0x00, 0xdc, 0x76, 0x3d, 0x27, 0xd8, 0xa5, + 0x65, 0x53, 0x45, 0x46, 0xf0, 0xb9, 0xf6, 0x04, 0xe7, 0x14, 0x3e, 0x27, 0xab, 0x3e, 0x2c, 0x06, + 0x60, 0x8d, 0xe8, 0xf4, 0xcb, 0x30, 0xa8, 0x90, 0x0f, 0xc3, 0x39, 0x4e, 0x7f, 0x0c, 0xc6, 0x12, + 0x6d, 0x75, 0xaa, 0x3e, 0xac, 0x33, 0x9e, 0xbf, 0xc7, 0x8e, 0x0c, 0xd1, 0xeb, 0x45, 0x6f, 0x47, + 0xdc, 0x4d, 0xf7, 0xe0, 0x58, 0x3d, 0xe3, 0xc8, 0x17, 0xf3, 0xda, 0xfd, 0x15, 0x71, 0x5a, 0x7c, + 0xf6, 0xb1, 0x2c, 0x28, 0xce, 0x6c, 0xc3, 0x38, 0x11, 0x0b, 0xed, 0x4e, 0x44, 0x7a, 0xde, 0x1d, + 0x53, 0x9d, 0xbf, 0x46, 0x76, 0xd5, 0xa1, 0xfa, 0x9d, 0xec, 0xfe, 0x19, 0x3e, 0xfa, 0xfc, 0xb8, + 0x1c, 0x12, 0x04, 0x8a, 0xd7, 0xc8, 0x2e, 0x9f, 0x0a, 0xfd, 0xeb, 0x8a, 0x6d, 0xbf, 0xee, 0x6b, + 0x16, 0x8c, 0xa8, 0xaf, 0x3b, 0x82, 0x73, 0x61, 0xce, 0x3c, 0x17, 0xce, 0xb4, 0x5d, 0xe0, 0x39, + 0x27, 0xc2, 0x37, 0x0a, 0x70, 0x4a, 0xe1, 0xd0, 0x47, 0x14, 0xff, 0x23, 0x56, 0xd5, 0x45, 0x18, + 0xf4, 0x94, 0x38, 0xd1, 0x32, 0xe5, 0x78, 0xb1, 0x30, 0x31, 0xc6, 0xa1, 0x57, 0x9e, 0x17, 0x5f, + 0xda, 0xc3, 0xba, 0x9c, 0x5d, 0x5c, 0xee, 0x73, 0x50, 0x6c, 0xb9, 0x35, 0x71, 0xc1, 0x7c, 0x58, + 0x8e, 0xf6, 0xfa, 0xf2, 0xc2, 0xc1, 0x5e, 0xe9, 0xd1, 0x3c, 0x95, 0x13, 0xbd, 0xd9, 0xc2, 0x99, + 0xf5, 0xe5, 0x05, 0x4c, 0x2b, 0xa3, 0x59, 0x18, 0x93, 0x5a, 0xb5, 0x9b, 0x94, 0x2f, 0xf5, 0x3d, + 0x71, 0x0f, 0x29, 0x61, 0x39, 0x36, 0xc1, 0x38, 0x89, 0x8f, 0x16, 0x60, 0x7c, 0xbb, 0x75, 0x9b, + 0xd4, 0x49, 0xc4, 0x3f, 0xf8, 0x1a, 0xe1, 0xa2, 0xe4, 0xc1, 0xf8, 0x09, 0x7b, 0x2d, 0x01, 0xc7, + 0xa9, 0x1a, 0xf6, 0xbf, 0xb2, 0xfb, 0x40, 0x8c, 0x9e, 0xc6, 0xdf, 0x7c, 0x27, 0x97, 0x73, 0x37, + 0xab, 0xe2, 0x1a, 0xd9, 0x5d, 0xf3, 0x29, 0x1f, 0x92, 0xbd, 0x2a, 0x8c, 0x35, 0xdf, 0xd3, 0x76, + 0xcd, 0xff, 0x56, 0x01, 0x8e, 0xab, 0x11, 0x30, 0xb8, 0xe5, 0xef, 0xf6, 0x31, 0xb8, 0x04, 0x43, + 0x35, 0xb2, 0xe1, 0xb4, 0xea, 0x91, 0xd2, 0x6b, 0xf4, 0x72, 0x55, 0xdb, 0x42, 0x5c, 0x8c, 0x75, + 0x9c, 0x43, 0x0c, 0xdb, 0xaf, 0x8f, 0xb0, 0x8b, 0x38, 0x72, 0xe8, 0x1a, 0x57, 0xbb, 0xc6, 0xca, + 0xdd, 0x35, 0x8f, 0x41, 0xaf, 0xdb, 0xa0, 0x8c, 0x59, 0xc1, 0xe4, 0xb7, 0x96, 0x69, 0x21, 0xe6, + 0x30, 0xf4, 0x04, 0xf4, 0x57, 0xfd, 0x46, 0xc3, 0xf1, 0x6a, 0xec, 0xca, 0x1b, 0x9c, 0x1b, 0xa2, + 0xbc, 0xdb, 0x3c, 0x2f, 0xc2, 0x12, 0x46, 0x99, 0x6f, 0x27, 0xd8, 0xe4, 0xc2, 0x1e, 0xc1, 0x7c, + 0xcf, 0x06, 0x9b, 0x21, 0x66, 0xa5, 0xf4, 0xad, 0x7a, 0xc7, 0x0f, 0xb6, 0x5d, 0x6f, 0x73, 0xc1, + 0x0d, 0xc4, 0x96, 0x50, 0x77, 0xe1, 0x2d, 0x05, 0xc1, 0x1a, 0x16, 0x5a, 0x82, 0xde, 0xa6, 0x1f, + 0x44, 0xe1, 0x54, 0x1f, 0x1b, 0xee, 0x47, 0x73, 0x0e, 0x22, 0xfe, 0xb5, 0x65, 0x3f, 0x88, 0xe2, + 0x0f, 0xa0, 0xff, 0x42, 0xcc, 0xab, 0xa3, 0xeb, 0xd0, 0x4f, 0xbc, 0x9d, 0xa5, 0xc0, 0x6f, 0x4c, + 0x4d, 0xe6, 0x53, 0x5a, 0xe4, 0x28, 0x7c, 0x99, 0xc5, 0x3c, 0xaa, 0x28, 0xc6, 0x92, 0x04, 0xfa, + 0x28, 0x14, 0x89, 0xb7, 0x33, 0xd5, 0xcf, 0x28, 0x4d, 0xe7, 0x50, 0xba, 0xe9, 0x04, 0xf1, 0x99, + 0xbf, 0xe8, 0xed, 0x60, 0x5a, 0x07, 0x7d, 0x02, 0x06, 0xe5, 0x81, 0x11, 0x0a, 0x29, 0x6a, 0xe6, + 0x82, 0x95, 0xc7, 0x0c, 0x26, 0xef, 0xb6, 0xdc, 0x80, 0x34, 0x88, 0x17, 0x85, 0xf1, 0x09, 0x29, + 0xa1, 0x21, 0x8e, 0xa9, 0xa1, 0x2a, 0x0c, 0x07, 0x24, 0x74, 0xef, 0x91, 0xb2, 0x5f, 0x77, 0xab, + 0xbb, 0x53, 0x27, 0x59, 0xf7, 0x9e, 0x6a, 0x3b, 0x64, 0x58, 0xab, 0x10, 0x4b, 0xf9, 0xf5, 0x52, + 0x6c, 0x10, 0x45, 0x6f, 0xc1, 0x48, 0x40, 0xc2, 0xc8, 0x09, 0x22, 0xd1, 0xca, 0x94, 0xd2, 0xca, + 0x8d, 0x60, 0x1d, 0xc0, 0x9f, 0x13, 0x71, 0x33, 0x31, 0x04, 0x9b, 0x14, 0xd0, 0x27, 0xa4, 0xca, + 0x61, 0xc5, 0x6f, 0x79, 0x51, 0x38, 0x35, 0xc8, 0xfa, 0x9d, 0xa9, 0x9b, 0xbe, 0x19, 0xe3, 0x25, + 0x75, 0x12, 0xbc, 0x32, 0x36, 0x48, 0xa1, 0x4f, 0xc1, 0x08, 0xff, 0xcf, 0x55, 0xaa, 0xe1, 0xd4, + 0x71, 0x46, 0xfb, 0x5c, 0x3e, 0x6d, 0x8e, 0x38, 0x77, 0x5c, 0x10, 0x1f, 0xd1, 0x4b, 0x43, 0x6c, + 0x52, 0x43, 0x18, 0x46, 0xea, 0xee, 0x0e, 0xf1, 0x48, 0x18, 0x96, 0x03, 0xff, 0x36, 0x11, 0x12, + 0xe2, 0x53, 0xd9, 0x2a, 0x58, 0xff, 0x36, 0x11, 0x8f, 0x40, 0xbd, 0x0e, 0x36, 0x49, 0xa0, 0x75, + 0x18, 0xa5, 0x4f, 0x72, 0x37, 0x26, 0x3a, 0xd4, 0x89, 0x28, 0x7b, 0x38, 0x63, 0xa3, 0x12, 0x4e, + 0x10, 0x41, 0x37, 0x60, 0x98, 0x8d, 0x79, 0xab, 0xc9, 0x89, 0x9e, 0xe8, 0x44, 0x94, 0x19, 0x14, + 0x54, 0xb4, 0x2a, 0xd8, 0x20, 0x80, 0xde, 0x84, 0xc1, 0xba, 0xbb, 0x41, 0xaa, 0xbb, 0xd5, 0x3a, + 0x99, 0x1a, 0x66, 0xd4, 0x32, 0x0f, 0xc3, 0xeb, 0x12, 0x89, 0xf3, 0xe7, 0xea, 0x2f, 0x8e, 0xab, + 0xa3, 0x9b, 0x70, 0x22, 0x22, 0x41, 0xc3, 0xf5, 0x1c, 0x7a, 0x88, 0x89, 0x27, 0x21, 0xd3, 0x8c, + 0x8f, 0xb0, 0xd5, 0x75, 0x56, 0xcc, 0xc6, 0x89, 0xb5, 0x4c, 0x2c, 0x9c, 0x53, 0x1b, 0xdd, 0x85, + 0xa9, 0x0c, 0x08, 0x5f, 0xb7, 0xc7, 0x18, 0xe5, 0xd7, 0x04, 0xe5, 0xa9, 0xb5, 0x1c, 0xbc, 0x83, + 0x36, 0x30, 0x9c, 0x4b, 0x1d, 0xdd, 0x80, 0x31, 0x76, 0x72, 0x96, 0x5b, 0xf5, 0xba, 0x68, 0x70, + 0x94, 0x35, 0xf8, 0x84, 0xe4, 0x23, 0x96, 0x4d, 0xf0, 0xc1, 0x5e, 0x09, 0xe2, 0x7f, 0x38, 0x59, + 0x1b, 0xdd, 0x66, 0x4a, 0xd8, 0x56, 0xe0, 0x46, 0xbb, 0x74, 0x57, 0x91, 0xbb, 0xd1, 0xd4, 0x58, + 0x5b, 0x81, 0x94, 0x8e, 0xaa, 0x34, 0xb5, 0x7a, 0x21, 0x4e, 0x12, 0xa4, 0x57, 0x41, 0x18, 0xd5, + 0x5c, 0x6f, 0x6a, 0x9c, 0xbf, 0xa7, 0xe4, 0x49, 0x5a, 0xa1, 0x85, 0x98, 0xc3, 0x98, 0x02, 0x96, + 0xfe, 0xb8, 0x41, 0x6f, 0xdc, 0x09, 0x86, 0x18, 0x2b, 0x60, 0x25, 0x00, 0xc7, 0x38, 0x94, 0x09, + 0x8e, 0xa2, 0xdd, 0x29, 0xc4, 0x50, 0xd5, 0x81, 0xb8, 0xb6, 0xf6, 0x09, 0x4c, 0xcb, 0xed, 0xdb, + 0x30, 0xaa, 0x8e, 0x09, 0x36, 0x26, 0xa8, 0x04, 0xbd, 0x8c, 0xed, 0x13, 0xe2, 0xd3, 0x41, 0xda, + 0x05, 0xc6, 0x12, 0x62, 0x5e, 0xce, 0xba, 0xe0, 0xde, 0x23, 0x73, 0xbb, 0x11, 0xe1, 0xb2, 0x88, + 0xa2, 0xd6, 0x05, 0x09, 0xc0, 0x31, 0x8e, 0xfd, 0x1f, 0x39, 0xfb, 0x1c, 0xdf, 0x12, 0x5d, 0xdc, + 0x8b, 0xcf, 0xc2, 0x00, 0x33, 0xfc, 0xf0, 0x03, 0xae, 0x9d, 0xed, 0x8d, 0x19, 0xe6, 0xab, 0xa2, + 0x1c, 0x2b, 0x0c, 0xf4, 0x2a, 0x8c, 0x54, 0xf5, 0x06, 0xc4, 0xa5, 0xae, 0x8e, 0x11, 0xa3, 0x75, + 0x6c, 0xe2, 0xa2, 0xcb, 0x30, 0xc0, 0x6c, 0x9c, 0xaa, 0x7e, 0x5d, 0x70, 0x9b, 0x92, 0x33, 0x19, + 0x28, 0x8b, 0xf2, 0x03, 0xed, 0x37, 0x56, 0xd8, 0xe8, 0x3c, 0xf4, 0xd1, 0x2e, 0x2c, 0x97, 0xc5, + 0x75, 0xaa, 0x24, 0x81, 0x57, 0x59, 0x29, 0x16, 0x50, 0xfb, 0x0f, 0x2c, 0xc6, 0x4b, 0xa5, 0xcf, + 0x7c, 0x74, 0x95, 0x5d, 0x1a, 0xec, 0x06, 0xd1, 0xb4, 0xf0, 0x8f, 0x6b, 0x37, 0x81, 0x82, 0x1d, + 0x24, 0xfe, 0x63, 0xa3, 0x26, 0x7a, 0x3b, 0x79, 0x33, 0x70, 0x86, 0xe2, 0x45, 0x39, 0x04, 0xc9, + 0xdb, 0xe1, 0x91, 0xf8, 0x8a, 0xa3, 0xfd, 0x69, 0x77, 0x45, 0xd8, 0x3f, 0x55, 0xd0, 0x56, 0x49, + 0x25, 0x72, 0x22, 0x82, 0xca, 0xd0, 0x7f, 0xc7, 0x71, 0x23, 0xd7, 0xdb, 0x14, 0x7c, 0x5f, 0xfb, + 0x8b, 0x8e, 0x55, 0xba, 0xc5, 0x2b, 0x70, 0xee, 0x45, 0xfc, 0xc1, 0x92, 0x0c, 0xa5, 0x18, 0xb4, + 0x3c, 0x8f, 0x52, 0x2c, 0x74, 0x4b, 0x11, 0xf3, 0x0a, 0x9c, 0xa2, 0xf8, 0x83, 0x25, 0x19, 0xf4, + 0x0e, 0x80, 0x3c, 0x21, 0x48, 0x4d, 0xc8, 0x0e, 0x9f, 0xed, 0x4c, 0x74, 0x4d, 0xd5, 0xe1, 0xc2, + 0xc9, 0xf8, 0x3f, 0xd6, 0xe8, 0xd9, 0x91, 0x36, 0xa7, 0x7a, 0x67, 0xd0, 0x27, 0xe9, 0x16, 0x75, + 0x82, 0x88, 0xd4, 0x66, 0x23, 0x31, 0x38, 0x4f, 0x77, 0xf7, 0x38, 0x5c, 0x73, 0x1b, 0x44, 0xdf, + 0xce, 0x82, 0x08, 0x8e, 0xe9, 0xd9, 0xbf, 0x53, 0x84, 0xa9, 0xbc, 0xee, 0xd2, 0x4d, 0x43, 0xee, + 0xba, 0xd1, 0x3c, 0x65, 0x6b, 0x2d, 0x73, 0xd3, 0x2c, 0x8a, 0x72, 0xac, 0x30, 0xe8, 0xea, 0x0d, + 0xdd, 0x4d, 0xf9, 0xb6, 0xef, 0x8d, 0x57, 0x6f, 0x85, 0x95, 0x62, 0x01, 0xa5, 0x78, 0x01, 0x71, + 0x42, 0x61, 0x7c, 0xa7, 0xad, 0x72, 0xcc, 0x4a, 0xb1, 0x80, 0xea, 0x52, 0xc6, 0x9e, 0x0e, 0x52, + 0x46, 0x63, 0x88, 0x7a, 0x1f, 0xec, 0x10, 0xa1, 0x4f, 0x03, 0x6c, 0xb8, 0x9e, 0x1b, 0x6e, 0x31, + 0xea, 0x7d, 0x87, 0xa6, 0xae, 0x98, 0xe2, 0x25, 0x45, 0x05, 0x6b, 0x14, 0xd1, 0x4b, 0x30, 0xa4, + 0x0e, 0x90, 0xe5, 0x05, 0xa6, 0xfa, 0xd7, 0x4c, 0xa9, 0xe2, 0xd3, 0x74, 0x01, 0xeb, 0x78, 0xf6, + 0x67, 0x93, 0xeb, 0x45, 0xec, 0x00, 0x6d, 0x7c, 0xad, 0x6e, 0xc7, 0xb7, 0xd0, 0x7e, 0x7c, 0xed, + 0x9f, 0x19, 0x84, 0x31, 0xa3, 0xb1, 0x56, 0xd8, 0xc5, 0x99, 0x7b, 0x85, 0x5e, 0x40, 0x4e, 0x44, + 0xc4, 0xfe, 0xb3, 0x3b, 0x6f, 0x15, 0xfd, 0x92, 0xa2, 0x3b, 0x80, 0xd7, 0x47, 0x9f, 0x86, 0xc1, + 0xba, 0x13, 0x32, 0x89, 0x25, 0x11, 0xfb, 0xae, 0x1b, 0x62, 0xf1, 0x83, 0xd0, 0x09, 0x23, 0xed, + 0xd6, 0xe7, 0xb4, 0x63, 0x92, 0xf4, 0xa6, 0xa4, 0xfc, 0x95, 0xb4, 0xee, 0x54, 0x9d, 0xa0, 0x4c, + 0xd8, 0x2e, 0xe6, 0x30, 0x74, 0x99, 0x1d, 0xad, 0x74, 0x55, 0xcc, 0x53, 0x6e, 0x94, 0x2d, 0xb3, + 0x5e, 0x83, 0xc9, 0x56, 0x30, 0x6c, 0x60, 0xc6, 0x6f, 0xb2, 0xbe, 0x36, 0x6f, 0xb2, 0xa7, 0xa0, + 0x9f, 0xfd, 0x50, 0x2b, 0x40, 0xcd, 0xc6, 0x32, 0x2f, 0xc6, 0x12, 0x9e, 0x5c, 0x30, 0x03, 0xdd, + 0x2d, 0x18, 0xfa, 0xea, 0x13, 0x8b, 0x9a, 0x99, 0x5d, 0x0c, 0xf0, 0x53, 0x4e, 0x2c, 0x79, 0x2c, + 0x61, 0xe8, 0x57, 0x2c, 0x40, 0x4e, 0x9d, 0xbe, 0x96, 0x69, 0xb1, 0x7a, 0xdc, 0x00, 0x63, 0xb5, + 0x5f, 0xed, 0x38, 0xec, 0xad, 0x70, 0x66, 0x36, 0x55, 0x9b, 0x4b, 0x4a, 0x5f, 0x11, 0x5d, 0x44, + 0x69, 0x04, 0xfd, 0x32, 0xba, 0xee, 0x86, 0xd1, 0xe7, 0xff, 0x26, 0x71, 0x39, 0x65, 0x74, 0x09, + 0xad, 0xeb, 0x8f, 0xaf, 0xa1, 0x43, 0x3e, 0xbe, 0x46, 0x72, 0x1f, 0x5e, 0xdf, 0x9f, 0x78, 0xc0, + 0x0c, 0xb3, 0x2f, 0x7f, 0xa2, 0xc3, 0x03, 0x46, 0x88, 0xd3, 0xbb, 0x79, 0xc6, 0x94, 0x85, 0x1e, + 0x78, 0x84, 0x75, 0xb9, 0xfd, 0x23, 0x78, 0x3d, 0x24, 0xc1, 0xdc, 0x29, 0xa9, 0x26, 0x3e, 0xd0, + 0x79, 0x0f, 0x4d, 0x6f, 0xfc, 0x43, 0x16, 0x4c, 0xa5, 0x07, 0x88, 0x77, 0x69, 0x6a, 0x94, 0xf5, + 0xdf, 0x6e, 0x37, 0x32, 0xa2, 0xf3, 0xd2, 0xdc, 0x75, 0x6a, 0x36, 0x87, 0x16, 0xce, 0x6d, 0x65, + 0xba, 0x05, 0x27, 0x73, 0xe6, 0x3d, 0x43, 0x6a, 0xbd, 0xa0, 0x4b, 0xad, 0x3b, 0xc8, 0x3a, 0x67, + 0xe4, 0xcc, 0xcc, 0xbc, 0xd5, 0x72, 0xbc, 0xc8, 0x8d, 0x76, 0x75, 0x29, 0xb7, 0x07, 0xe6, 0x80, + 0xa0, 0x4f, 0x41, 0x6f, 0xdd, 0xf5, 0x5a, 0x77, 0xc5, 0x4d, 0x79, 0x3e, 0xfb, 0x11, 0xe3, 0xb5, + 0xee, 0x9a, 0x43, 0x5c, 0xa2, 0x1b, 0x92, 0x95, 0x1f, 0xec, 0x95, 0x50, 0x1a, 0x01, 0x73, 0xaa, + 0xf6, 0xd3, 0x30, 0xba, 0xe0, 0x90, 0x86, 0xef, 0x2d, 0x7a, 0xb5, 0xa6, 0xef, 0x7a, 0x11, 0x9a, + 0x82, 0x1e, 0xc6, 0x22, 0xf2, 0x0b, 0xb2, 0x87, 0x0e, 0x21, 0x66, 0x25, 0xf6, 0x26, 0x1c, 0x5f, + 0xf0, 0xef, 0x78, 0x77, 0x9c, 0xa0, 0x36, 0x5b, 0x5e, 0xd6, 0xa4, 0x7e, 0xab, 0x52, 0xea, 0x64, + 0xe5, 0xbf, 0xe9, 0xb5, 0x9a, 0x7c, 0x29, 0x2d, 0xb9, 0x75, 0x92, 0x23, 0x9b, 0xfd, 0x99, 0x82, + 0xd1, 0x52, 0x8c, 0xaf, 0x34, 0x8b, 0x56, 0xae, 0x51, 0xc2, 0x5b, 0x30, 0xb0, 0xe1, 0x92, 0x7a, + 0x0d, 0x93, 0x0d, 0x31, 0x1b, 0x4f, 0xe6, 0x9b, 0x2d, 0x2e, 0x51, 0x4c, 0xa5, 0x02, 0x65, 0x32, + 0xab, 0x25, 0x51, 0x19, 0x2b, 0x32, 0x68, 0x1b, 0xc6, 0xe5, 0x9c, 0x49, 0xa8, 0x38, 0xb5, 0x9f, + 0x6a, 0xb7, 0x08, 0x4d, 0xe2, 0xcc, 0x84, 0x1b, 0x27, 0xc8, 0xe0, 0x14, 0x61, 0x74, 0x1a, 0x7a, + 0x1a, 0x94, 0x3f, 0xe9, 0x61, 0xc3, 0xcf, 0x84, 0x54, 0x4c, 0xde, 0xc6, 0x4a, 0xed, 0x9f, 0xb3, + 0xe0, 0x64, 0x6a, 0x64, 0x84, 0xdc, 0xf1, 0x01, 0xcf, 0x42, 0x52, 0x0e, 0x58, 0xe8, 0x2c, 0x07, + 0xb4, 0xff, 0x3b, 0x0b, 0x8e, 0x2d, 0x36, 0x9a, 0xd1, 0xee, 0x82, 0x6b, 0x5a, 0x10, 0xbc, 0x0c, + 0x7d, 0x0d, 0x52, 0x73, 0x5b, 0x0d, 0x31, 0x73, 0x25, 0x79, 0x87, 0xaf, 0xb0, 0x52, 0x7a, 0x0e, + 0x54, 0x22, 0x3f, 0x70, 0x36, 0x09, 0x2f, 0xc0, 0x02, 0x9d, 0x71, 0x42, 0xee, 0x3d, 0x72, 0xdd, + 0x6d, 0xb8, 0xd1, 0xfd, 0xed, 0x2e, 0xa1, 0xfc, 0x97, 0x44, 0x70, 0x4c, 0xcf, 0xfe, 0x96, 0x05, + 0x63, 0x72, 0xdd, 0xcf, 0xd6, 0x6a, 0x01, 0x09, 0x43, 0x34, 0x0d, 0x05, 0xb7, 0x29, 0x7a, 0x09, + 0xa2, 0x97, 0x85, 0xe5, 0x32, 0x2e, 0xb8, 0x4d, 0xf9, 0xe8, 0x62, 0x6c, 0x42, 0xd1, 0xb4, 0x83, + 0xb8, 0x2a, 0xca, 0xb1, 0xc2, 0x40, 0x17, 0x60, 0xc0, 0xf3, 0x6b, 0xfc, 0xdd, 0x22, 0x34, 0xe1, + 0x14, 0x73, 0x55, 0x94, 0x61, 0x05, 0x45, 0x65, 0x18, 0xe4, 0x56, 0xb2, 0xf1, 0xa2, 0xed, 0xca, + 0xd6, 0x96, 0x7d, 0xd9, 0x9a, 0xac, 0x89, 0x63, 0x22, 0xf6, 0x1f, 0x59, 0x30, 0x2c, 0xbf, 0xac, + 0xcb, 0x17, 0x25, 0xdd, 0x5a, 0xf1, 0x6b, 0x32, 0xde, 0x5a, 0xf4, 0x45, 0xc8, 0x20, 0xc6, 0x43, + 0xb0, 0x78, 0xa8, 0x87, 0xe0, 0x25, 0x18, 0x72, 0x9a, 0xcd, 0xb2, 0xf9, 0x8a, 0x64, 0x4b, 0x69, + 0x36, 0x2e, 0xc6, 0x3a, 0x8e, 0xfd, 0xb3, 0x05, 0x18, 0x95, 0x5f, 0x50, 0x69, 0xdd, 0x0e, 0x49, + 0x84, 0xd6, 0x60, 0xd0, 0xe1, 0xb3, 0x44, 0xe4, 0x22, 0x7f, 0x2c, 0x5b, 0xba, 0x69, 0x4c, 0x69, + 0xcc, 0x0e, 0xcf, 0xca, 0xda, 0x38, 0x26, 0x84, 0xea, 0x30, 0xe1, 0xf9, 0x11, 0x63, 0x8d, 0x14, + 0xbc, 0x9d, 0xc2, 0x39, 0x49, 0xfd, 0x94, 0xa0, 0x3e, 0xb1, 0x9a, 0xa4, 0x82, 0xd3, 0x84, 0xd1, + 0xa2, 0x94, 0x18, 0x17, 0xf3, 0x45, 0x7d, 0xfa, 0xc4, 0x65, 0x0b, 0x8c, 0xed, 0xdf, 0xb7, 0x60, + 0x50, 0xa2, 0x1d, 0x85, 0x6d, 0xc1, 0x0a, 0xf4, 0x87, 0x6c, 0x12, 0xe4, 0xd0, 0xd8, 0xed, 0x3a, + 0xce, 0xe7, 0x2b, 0xe6, 0xf8, 0xf8, 0xff, 0x10, 0x4b, 0x1a, 0x4c, 0x61, 0xa8, 0xba, 0xff, 0x3e, + 0x51, 0x18, 0xaa, 0xfe, 0xe4, 0x5c, 0x4a, 0x7f, 0xc7, 0xfa, 0xac, 0x49, 0xe0, 0xe9, 0xc3, 0xa4, + 0x19, 0x90, 0x0d, 0xf7, 0x6e, 0xf2, 0x61, 0x52, 0x66, 0xa5, 0x58, 0x40, 0xd1, 0x3b, 0x30, 0x5c, + 0x95, 0x9a, 0xa2, 0x78, 0x87, 0x9f, 0x6f, 0xab, 0xb5, 0x54, 0x0a, 0x6e, 0x2e, 0xe9, 0x9c, 0xd7, + 0xea, 0x63, 0x83, 0x9a, 0x69, 0x05, 0x56, 0xec, 0x64, 0x05, 0x16, 0xd3, 0xcd, 0xb7, 0x89, 0xfa, + 0x79, 0x0b, 0xfa, 0xb8, 0x86, 0xa0, 0x3b, 0x05, 0x8d, 0xa6, 0xef, 0x8f, 0xc7, 0xee, 0x26, 0x2d, + 0x14, 0x9c, 0x0d, 0x5a, 0x81, 0x41, 0xf6, 0x83, 0x69, 0x38, 0x8a, 0xf9, 0x3e, 0x63, 0xbc, 0x55, + 0xbd, 0x83, 0x37, 0x65, 0x35, 0x1c, 0x53, 0xb0, 0x7f, 0xba, 0x48, 0x4f, 0xb7, 0x18, 0xd5, 0xb8, + 0xf4, 0xad, 0x87, 0x77, 0xe9, 0x17, 0x1e, 0xd6, 0xa5, 0xbf, 0x09, 0x63, 0x55, 0xcd, 0x3a, 0x20, + 0x9e, 0xc9, 0x0b, 0x6d, 0x17, 0x89, 0x66, 0x48, 0xc0, 0x65, 0xa8, 0xf3, 0x26, 0x11, 0x9c, 0xa4, + 0x8a, 0x3e, 0x09, 0xc3, 0x7c, 0x9e, 0x45, 0x2b, 0xdc, 0x90, 0xee, 0x89, 0xfc, 0xf5, 0xa2, 0x37, + 0xc1, 0x65, 0xee, 0x5a, 0x75, 0x6c, 0x10, 0xb3, 0xff, 0xc9, 0x02, 0xb4, 0xd8, 0xdc, 0x22, 0x0d, + 0x12, 0x38, 0xf5, 0x58, 0xc9, 0xf7, 0x25, 0x0b, 0xa6, 0x48, 0xaa, 0x78, 0xde, 0x6f, 0x34, 0xc4, + 0x93, 0x3e, 0x47, 0xea, 0xb4, 0x98, 0x53, 0x27, 0x66, 0xeb, 0xf3, 0x30, 0x70, 0x6e, 0x7b, 0x68, + 0x05, 0x26, 0xf9, 0x2d, 0xa9, 0x00, 0x9a, 0xad, 0xdd, 0x23, 0x82, 0xf0, 0xe4, 0x5a, 0x1a, 0x05, + 0x67, 0xd5, 0xb3, 0x7f, 0x7f, 0x04, 0x72, 0x7b, 0xf1, 0x81, 0x76, 0xf3, 0x03, 0xed, 0xe6, 0x07, + 0xda, 0xcd, 0x0f, 0xb4, 0x9b, 0x1f, 0x68, 0x37, 0x3f, 0xd0, 0x6e, 0xbe, 0x4f, 0xb5, 0x9b, 0xff, + 0xa5, 0x05, 0xc7, 0xd5, 0xf5, 0x65, 0x3c, 0xd8, 0x3f, 0x07, 0x93, 0x7c, 0xbb, 0xcd, 0xd7, 0x1d, + 0xb7, 0xb1, 0x46, 0x1a, 0xcd, 0xba, 0x13, 0x49, 0x1b, 0xa6, 0x4b, 0x99, 0x2b, 0x37, 0xe1, 0x28, + 0x61, 0x54, 0xe4, 0x1e, 0x67, 0x19, 0x00, 0x9c, 0xd5, 0x8c, 0xfd, 0x3b, 0x03, 0xd0, 0xbb, 0xb8, + 0x43, 0xbc, 0xe8, 0x08, 0x9e, 0x36, 0x55, 0x18, 0x75, 0xbd, 0x1d, 0xbf, 0xbe, 0x43, 0x6a, 0x1c, + 0x7e, 0x98, 0x17, 0xf8, 0x09, 0x41, 0x7a, 0x74, 0xd9, 0x20, 0x81, 0x13, 0x24, 0x1f, 0x86, 0x8e, + 0xe8, 0x0a, 0xf4, 0xf1, 0xcb, 0x47, 0x28, 0x88, 0x32, 0xcf, 0x6c, 0x36, 0x88, 0xe2, 0x4a, 0x8d, + 0xf5, 0x57, 0xfc, 0x72, 0x13, 0xd5, 0xd1, 0x67, 0x61, 0x74, 0xc3, 0x0d, 0xc2, 0x68, 0xcd, 0x6d, + 0xd0, 0xab, 0xa1, 0xd1, 0xbc, 0x0f, 0x9d, 0x90, 0x1a, 0x87, 0x25, 0x83, 0x12, 0x4e, 0x50, 0x46, + 0x9b, 0x30, 0x52, 0x77, 0xf4, 0xa6, 0xfa, 0x0f, 0xdd, 0x94, 0xba, 0x1d, 0xae, 0xeb, 0x84, 0xb0, + 0x49, 0x97, 0x6e, 0xa7, 0x2a, 0x53, 0x6b, 0x0c, 0x30, 0x71, 0x86, 0xda, 0x4e, 0x5c, 0x9f, 0xc1, + 0x61, 0x94, 0x41, 0x63, 0xee, 0x06, 0x83, 0x26, 0x83, 0xa6, 0x39, 0x15, 0x7c, 0x06, 0x06, 0x09, + 0x1d, 0x42, 0x4a, 0x58, 0x5c, 0x30, 0x17, 0xbb, 0xeb, 0xeb, 0x8a, 0x5b, 0x0d, 0x7c, 0x53, 0x1b, + 0xb7, 0x28, 0x29, 0xe1, 0x98, 0x28, 0x9a, 0x87, 0xbe, 0x90, 0x04, 0xae, 0x92, 0xf8, 0xb7, 0x99, + 0x46, 0x86, 0xc6, 0x5d, 0x1a, 0xf9, 0x6f, 0x2c, 0xaa, 0xd2, 0xe5, 0xe5, 0x30, 0x51, 0x2c, 0xbb, + 0x0c, 0xb4, 0xe5, 0x35, 0xcb, 0x4a, 0xb1, 0x80, 0xa2, 0x37, 0xa1, 0x3f, 0x20, 0x75, 0xa6, 0xee, + 0x1d, 0xe9, 0x7e, 0x91, 0x73, 0xed, 0x31, 0xaf, 0x87, 0x25, 0x01, 0x74, 0x0d, 0x50, 0x40, 0x28, + 0x83, 0xe7, 0x7a, 0x9b, 0xca, 0x08, 0x5f, 0x1c, 0xb4, 0x8a, 0x91, 0xc6, 0x31, 0x86, 0xf4, 0x66, + 0xc5, 0x19, 0xd5, 0xd0, 0x15, 0x98, 0x50, 0xa5, 0xcb, 0x5e, 0x18, 0x39, 0xf4, 0x80, 0x1b, 0x63, + 0xb4, 0x94, 0x7c, 0x05, 0x27, 0x11, 0x70, 0xba, 0x8e, 0xfd, 0x6b, 0x16, 0xf0, 0x71, 0x3e, 0x02, + 0xa9, 0xc2, 0xeb, 0xa6, 0x54, 0xe1, 0x54, 0xee, 0xcc, 0xe5, 0x48, 0x14, 0x7e, 0xcd, 0x82, 0x21, + 0x6d, 0x66, 0xe3, 0x35, 0x6b, 0xb5, 0x59, 0xb3, 0x2d, 0x18, 0xa7, 0x2b, 0xfd, 0xc6, 0xed, 0x90, + 0x04, 0x3b, 0xa4, 0xc6, 0x16, 0x66, 0xe1, 0xfe, 0x16, 0xa6, 0x32, 0xf8, 0xbd, 0x9e, 0x20, 0x88, + 0x53, 0x4d, 0xd8, 0x9f, 0x91, 0x5d, 0x55, 0xf6, 0xd1, 0x55, 0x35, 0xe7, 0x09, 0xfb, 0x68, 0x35, + 0xab, 0x38, 0xc6, 0xa1, 0x5b, 0x6d, 0xcb, 0x0f, 0xa3, 0xa4, 0x7d, 0xf4, 0x55, 0x3f, 0x8c, 0x30, + 0x83, 0xd8, 0x2f, 0x00, 0x2c, 0xde, 0x25, 0x55, 0xbe, 0x62, 0xf5, 0x47, 0x8f, 0x95, 0xff, 0xe8, + 0xb1, 0xff, 0xd2, 0x82, 0xd1, 0xa5, 0x79, 0xe3, 0xe6, 0x9a, 0x01, 0xe0, 0x2f, 0xb5, 0x5b, 0xb7, + 0x56, 0xa5, 0x91, 0x0e, 0xb7, 0x53, 0x50, 0xa5, 0x58, 0xc3, 0x40, 0xa7, 0xa0, 0x58, 0x6f, 0x79, + 0x42, 0xec, 0xd9, 0x4f, 0xaf, 0xc7, 0xeb, 0x2d, 0x0f, 0xd3, 0x32, 0xcd, 0x93, 0xad, 0xd8, 0xb5, + 0x27, 0x5b, 0xc7, 0x80, 0x3a, 0xa8, 0x04, 0xbd, 0x77, 0xee, 0xb8, 0x35, 0x1e, 0x27, 0x40, 0x18, + 0x10, 0xdd, 0xba, 0xb5, 0xbc, 0x10, 0x62, 0x5e, 0x6e, 0x7f, 0xb9, 0x08, 0xd3, 0x4b, 0x75, 0x72, + 0xf7, 0x3d, 0xc6, 0x4a, 0xe8, 0xd6, 0x0f, 0xef, 0x70, 0x02, 0xa4, 0xc3, 0xfa, 0x5a, 0x76, 0x1e, + 0x8f, 0x0d, 0xe8, 0xe7, 0xe6, 0xc1, 0x32, 0x72, 0x42, 0xa6, 0x52, 0x36, 0x7f, 0x40, 0x66, 0xb8, + 0x99, 0xb1, 0x50, 0xca, 0xaa, 0x0b, 0x53, 0x94, 0x62, 0x49, 0x7c, 0xfa, 0x15, 0x18, 0xd6, 0x31, + 0x0f, 0xe5, 0xf5, 0xfc, 0xc3, 0x45, 0x18, 0xa7, 0x3d, 0x78, 0xa8, 0x13, 0xb1, 0x9e, 0x9e, 0x88, + 0x07, 0xed, 0xf9, 0xda, 0x79, 0x36, 0xde, 0x49, 0xce, 0xc6, 0xa5, 0xbc, 0xd9, 0x38, 0xea, 0x39, + 0xf8, 0x11, 0x0b, 0x26, 0x97, 0xea, 0x7e, 0x75, 0x3b, 0xe1, 0x9d, 0xfa, 0x12, 0x0c, 0xd1, 0xe3, + 0x38, 0x34, 0x02, 0xb5, 0x18, 0xa1, 0x7b, 0x04, 0x08, 0xeb, 0x78, 0x5a, 0xb5, 0xf5, 0xf5, 0xe5, + 0x85, 0xac, 0x88, 0x3f, 0x02, 0x84, 0x75, 0x3c, 0xfb, 0xcf, 0x2d, 0x38, 0x73, 0x65, 0x7e, 0x31, + 0x5e, 0x8a, 0xa9, 0xa0, 0x43, 0xe7, 0xa1, 0xaf, 0x59, 0xd3, 0xba, 0x12, 0x8b, 0x85, 0x17, 0x58, + 0x2f, 0x04, 0xf4, 0xfd, 0x12, 0xdf, 0x6b, 0x1d, 0xe0, 0x0a, 0x2e, 0xcf, 0x8b, 0x73, 0x57, 0x6a, + 0x81, 0xac, 0x5c, 0x2d, 0xd0, 0x13, 0xd0, 0x4f, 0xef, 0x05, 0xb7, 0x2a, 0xfb, 0xcd, 0xcd, 0x2e, + 0x78, 0x11, 0x96, 0x30, 0xfb, 0x57, 0x2d, 0x98, 0xbc, 0xe2, 0x46, 0xf4, 0xd2, 0x4e, 0x46, 0xd5, + 0xa1, 0xb7, 0x76, 0xe8, 0x46, 0x7e, 0xb0, 0x9b, 0x8c, 0xaa, 0x83, 0x15, 0x04, 0x6b, 0x58, 0xfc, + 0x83, 0x76, 0x5c, 0xe6, 0xef, 0x52, 0x30, 0xf5, 0x6e, 0x58, 0x94, 0x63, 0x85, 0x41, 0xc7, 0xab, + 0xe6, 0x06, 0x4c, 0x64, 0xb9, 0x2b, 0x0e, 0x6e, 0x35, 0x5e, 0x0b, 0x12, 0x80, 0x63, 0x1c, 0xfb, + 0x1f, 0x2c, 0x28, 0x5d, 0xe1, 0x5e, 0xbb, 0x1b, 0x61, 0xce, 0xa1, 0xfb, 0x02, 0x0c, 0x12, 0xa9, + 0x20, 0x10, 0xbd, 0x56, 0x8c, 0xa8, 0xd2, 0x1c, 0xf0, 0xe0, 0x3e, 0x0a, 0xaf, 0x0b, 0x17, 0xfa, + 0xc3, 0xf9, 0x40, 0x2f, 0x01, 0x22, 0x7a, 0x5b, 0x7a, 0xb4, 0x23, 0x16, 0x36, 0x65, 0x31, 0x05, + 0xc5, 0x19, 0x35, 0xec, 0x9f, 0xb3, 0xe0, 0xb8, 0xfa, 0xe0, 0xf7, 0xdd, 0x67, 0xda, 0x5f, 0x2f, + 0xc0, 0xc8, 0xd5, 0xb5, 0xb5, 0xf2, 0x15, 0x12, 0x69, 0xab, 0xb2, 0xbd, 0xda, 0x1f, 0x6b, 0xda, + 0xcb, 0x76, 0x6f, 0xc4, 0x56, 0xe4, 0xd6, 0x67, 0x78, 0x0c, 0xbf, 0x99, 0x65, 0x2f, 0xba, 0x11, + 0x54, 0xa2, 0xc0, 0xf5, 0x36, 0x33, 0x57, 0xba, 0xe4, 0x59, 0x8a, 0x79, 0x3c, 0x0b, 0x7a, 0x01, + 0xfa, 0x58, 0x10, 0x41, 0x39, 0x09, 0x8f, 0xa8, 0x27, 0x16, 0x2b, 0x3d, 0xd8, 0x2b, 0x0d, 0xae, + 0xe3, 0x65, 0xfe, 0x07, 0x0b, 0x54, 0xb4, 0x0e, 0x43, 0x5b, 0x51, 0xd4, 0xbc, 0x4a, 0x9c, 0x1a, + 0x09, 0xe4, 0x29, 0x7b, 0x36, 0xeb, 0x94, 0xa5, 0x83, 0xc0, 0xd1, 0xe2, 0x83, 0x29, 0x2e, 0x0b, + 0xb1, 0x4e, 0xc7, 0xae, 0x00, 0xc4, 0xb0, 0x07, 0xa4, 0xb8, 0xb1, 0xd7, 0x60, 0x90, 0x7e, 0xee, + 0x6c, 0xdd, 0x75, 0xda, 0xab, 0xc6, 0x9f, 0x81, 0x41, 0xa9, 0xf8, 0x0e, 0x45, 0x88, 0x0f, 0x76, + 0x23, 0x49, 0xbd, 0x78, 0x88, 0x63, 0xb8, 0xfd, 0x38, 0x08, 0x0b, 0xe0, 0x76, 0x24, 0xed, 0x0d, + 0x38, 0xc6, 0x4c, 0x99, 0x9d, 0x68, 0xcb, 0x58, 0xa3, 0x9d, 0x17, 0xc3, 0xb3, 0xe2, 0x5d, 0xc7, + 0xbf, 0x6c, 0x4a, 0x73, 0x21, 0x1f, 0x96, 0x14, 0xe3, 0x37, 0x9e, 0xfd, 0xf7, 0x3d, 0xf0, 0xc8, + 0x72, 0x25, 0x3f, 0x36, 0xd5, 0x65, 0x18, 0xe6, 0xec, 0x22, 0x5d, 0x1a, 0x4e, 0x5d, 0xb4, 0xab, + 0x24, 0xa0, 0x6b, 0x1a, 0x0c, 0x1b, 0x98, 0xe8, 0x0c, 0x14, 0xdd, 0x77, 0xbd, 0xa4, 0x83, 0xe5, + 0xf2, 0x5b, 0xab, 0x98, 0x96, 0x53, 0x30, 0xe5, 0x3c, 0xf9, 0x91, 0xae, 0xc0, 0x8a, 0xfb, 0x7c, + 0x1d, 0x46, 0xdd, 0xb0, 0x1a, 0xba, 0xcb, 0x1e, 0xdd, 0xa7, 0xda, 0x4e, 0x57, 0x32, 0x07, 0xda, + 0x69, 0x05, 0xc5, 0x09, 0x6c, 0xed, 0x7e, 0xe9, 0xed, 0x9a, 0x7b, 0xed, 0x18, 0x19, 0x83, 0x1e, + 0xff, 0x4d, 0xf6, 0x75, 0x21, 0x13, 0xc1, 0x8b, 0xe3, 0x9f, 0x7f, 0x70, 0x88, 0x25, 0x8c, 0x3e, + 0xe8, 0xaa, 0x5b, 0x4e, 0x73, 0xb6, 0x15, 0x6d, 0x2d, 0xb8, 0x61, 0xd5, 0xdf, 0x21, 0xc1, 0x2e, + 0x7b, 0x8b, 0x0f, 0xc4, 0x0f, 0x3a, 0x05, 0x98, 0xbf, 0x3a, 0x5b, 0xa6, 0x98, 0x38, 0x5d, 0x07, + 0xcd, 0xc2, 0x98, 0x2c, 0xac, 0x90, 0x90, 0x5d, 0x01, 0x43, 0x8c, 0x8c, 0x72, 0x79, 0x14, 0xc5, + 0x8a, 0x48, 0x12, 0xdf, 0x64, 0x70, 0xe1, 0x41, 0x30, 0xb8, 0x2f, 0xc3, 0x88, 0xeb, 0xb9, 0x91, + 0xeb, 0x44, 0x3e, 0xd7, 0x1f, 0xf1, 0x67, 0x37, 0x13, 0x30, 0x2f, 0xeb, 0x00, 0x6c, 0xe2, 0xd9, + 0xff, 0x5f, 0x0f, 0x4c, 0xb0, 0x69, 0xfb, 0x60, 0x85, 0x7d, 0x2f, 0xad, 0xb0, 0xf5, 0xf4, 0x0a, + 0x7b, 0x10, 0x9c, 0xfb, 0x7d, 0x2f, 0xb3, 0x2f, 0x58, 0x30, 0xc1, 0x64, 0xdc, 0xc6, 0x32, 0xbb, + 0x08, 0x83, 0x81, 0xe1, 0x8d, 0x3a, 0xa8, 0x2b, 0xb5, 0xa4, 0x63, 0x69, 0x8c, 0x83, 0xde, 0x00, + 0x68, 0xc6, 0x32, 0xf4, 0x82, 0x11, 0x42, 0x14, 0x72, 0xc5, 0xe7, 0x5a, 0x1d, 0xfb, 0xb3, 0x30, + 0xa8, 0xdc, 0x4d, 0xa5, 0xbf, 0xb9, 0x95, 0xe3, 0x6f, 0xde, 0x99, 0x8d, 0x90, 0xb6, 0x71, 0xc5, + 0x4c, 0xdb, 0xb8, 0xaf, 0x5a, 0x10, 0x6b, 0x38, 0xd0, 0x5b, 0x30, 0xd8, 0xf4, 0x99, 0x41, 0x74, + 0x20, 0xbd, 0x0c, 0x1e, 0x6f, 0xab, 0x22, 0xe1, 0x71, 0x02, 0x03, 0x3e, 0x1d, 0x65, 0x59, 0x15, + 0xc7, 0x54, 0xd0, 0x35, 0xe8, 0x6f, 0x06, 0xa4, 0x12, 0xb1, 0x20, 0x56, 0xdd, 0x13, 0xe4, 0xcb, + 0x97, 0x57, 0xc4, 0x92, 0x82, 0xfd, 0x1b, 0x05, 0x18, 0x4f, 0xa2, 0xa2, 0xd7, 0xa0, 0x87, 0xdc, + 0x25, 0x55, 0xd1, 0xdf, 0x4c, 0x9e, 0x20, 0x96, 0x91, 0xf0, 0x01, 0xa0, 0xff, 0x31, 0xab, 0x85, + 0xae, 0x42, 0x3f, 0x65, 0x08, 0xae, 0xa8, 0x80, 0x8d, 0x8f, 0xe6, 0x31, 0x15, 0x8a, 0xb3, 0xe2, + 0x9d, 0x13, 0x45, 0x58, 0x56, 0x67, 0x06, 0x69, 0xd5, 0x66, 0x85, 0xbe, 0xb5, 0xa2, 0x76, 0x22, + 0x81, 0xb5, 0xf9, 0x32, 0x47, 0x12, 0xd4, 0xb8, 0x41, 0x9a, 0x2c, 0xc4, 0x31, 0x11, 0xf4, 0x06, + 0xf4, 0x86, 0x75, 0x42, 0x9a, 0xc2, 0xe2, 0x20, 0x53, 0xca, 0x59, 0xa1, 0x08, 0x82, 0x12, 0x93, + 0x8a, 0xb0, 0x02, 0xcc, 0x2b, 0xda, 0xbf, 0x65, 0x01, 0x70, 0x0b, 0x3e, 0xc7, 0xdb, 0x24, 0x47, + 0xa0, 0x18, 0x58, 0x80, 0x9e, 0xb0, 0x49, 0xaa, 0xed, 0xac, 0xfd, 0xe3, 0xfe, 0x54, 0x9a, 0xa4, + 0x1a, 0xaf, 0x59, 0xfa, 0x0f, 0xb3, 0xda, 0xf6, 0x8f, 0x02, 0x8c, 0xc6, 0x68, 0xcb, 0x11, 0x69, + 0xa0, 0xe7, 0x8c, 0x28, 0x37, 0xa7, 0x12, 0x51, 0x6e, 0x06, 0x19, 0xb6, 0x26, 0x83, 0xfe, 0x2c, + 0x14, 0x1b, 0xce, 0x5d, 0x21, 0x64, 0x7c, 0xa6, 0x7d, 0x37, 0x28, 0xfd, 0x99, 0x15, 0xe7, 0x2e, + 0x7f, 0x87, 0x3f, 0x23, 0xf7, 0xd8, 0x8a, 0x73, 0xb7, 0xa3, 0x45, 0x3a, 0x6d, 0x84, 0xb5, 0xe5, + 0x7a, 0xc2, 0x38, 0xad, 0xab, 0xb6, 0x5c, 0x2f, 0xd9, 0x96, 0xeb, 0x75, 0xd1, 0x96, 0xeb, 0xa1, + 0x7b, 0xd0, 0x2f, 0x6c, 0x47, 0x45, 0xf8, 0xbd, 0x8b, 0x5d, 0xb4, 0x27, 0x4c, 0x4f, 0x79, 0x9b, + 0x17, 0xa5, 0x9c, 0x41, 0x94, 0x76, 0x6c, 0x57, 0x36, 0x88, 0xfe, 0x2b, 0x0b, 0x46, 0xc5, 0x6f, + 0x4c, 0xde, 0x6d, 0x91, 0x30, 0x12, 0x7c, 0xf8, 0x47, 0xba, 0xef, 0x83, 0xa8, 0xc8, 0xbb, 0xf2, + 0x11, 0x79, 0x65, 0x9a, 0xc0, 0x8e, 0x3d, 0x4a, 0xf4, 0x02, 0xfd, 0x86, 0x05, 0xc7, 0x1a, 0xce, + 0x5d, 0xde, 0x22, 0x2f, 0xc3, 0x4e, 0xe4, 0xfa, 0xc2, 0x06, 0xe3, 0xb5, 0xee, 0xa6, 0x3f, 0x55, + 0x9d, 0x77, 0x52, 0x2a, 0x5c, 0x8f, 0x65, 0xa1, 0x74, 0xec, 0x6a, 0x66, 0xbf, 0xa6, 0x37, 0x60, + 0x40, 0xae, 0xb7, 0x87, 0x69, 0x18, 0xcf, 0xda, 0x11, 0x6b, 0xed, 0xa1, 0xb6, 0xf3, 0x59, 0x18, + 0xd6, 0xd7, 0xd8, 0x43, 0x6d, 0xeb, 0x5d, 0x98, 0xcc, 0x58, 0x4b, 0x0f, 0xb5, 0xc9, 0x3b, 0x70, + 0x2a, 0x77, 0x7d, 0x3c, 0x54, 0xc7, 0x86, 0xaf, 0x5b, 0xfa, 0x39, 0x78, 0x04, 0xda, 0x99, 0x79, + 0x53, 0x3b, 0x73, 0xb6, 0xfd, 0xce, 0xc9, 0x51, 0xd1, 0xbc, 0xa3, 0x77, 0x9a, 0x9e, 0xea, 0xe8, + 0x4d, 0xe8, 0xab, 0xd3, 0x12, 0x69, 0x81, 0x6c, 0x77, 0xde, 0x91, 0x31, 0x5f, 0xcc, 0xca, 0x43, + 0x2c, 0x28, 0xd8, 0x5f, 0xb1, 0x20, 0xc3, 0x35, 0x83, 0xf2, 0x49, 0x2d, 0xb7, 0xc6, 0x86, 0xa4, + 0x18, 0xf3, 0x49, 0x2a, 0x08, 0xcc, 0x19, 0x28, 0x6e, 0xba, 0x35, 0xe1, 0x59, 0xac, 0xc0, 0x57, + 0x28, 0x78, 0xd3, 0xad, 0xa1, 0x25, 0x40, 0x61, 0xab, 0xd9, 0xac, 0x33, 0xb3, 0x25, 0xa7, 0x7e, + 0x25, 0xf0, 0x5b, 0x4d, 0x6e, 0x6e, 0x5c, 0xe4, 0x42, 0xa2, 0x4a, 0x0a, 0x8a, 0x33, 0x6a, 0xd8, + 0xbf, 0x6b, 0x41, 0xcf, 0x11, 0x4c, 0x13, 0x36, 0xa7, 0xe9, 0xb9, 0x5c, 0xd2, 0x22, 0x6b, 0xc3, + 0x0c, 0x76, 0xee, 0x2c, 0xde, 0x8d, 0x88, 0x17, 0x32, 0x86, 0x23, 0x73, 0xd6, 0xf6, 0x2c, 0x98, + 0xbc, 0xee, 0x3b, 0xb5, 0x39, 0xa7, 0xee, 0x78, 0x55, 0x12, 0x2c, 0x7b, 0x9b, 0x87, 0xb2, 0xed, + 0x2f, 0x74, 0xb4, 0xed, 0xbf, 0x0c, 0x7d, 0x6e, 0x53, 0x0b, 0xfb, 0x7e, 0x8e, 0xce, 0xee, 0x72, + 0x59, 0x44, 0x7c, 0x47, 0x46, 0xe3, 0xac, 0x14, 0x0b, 0x7c, 0xba, 0x2c, 0xb9, 0x51, 0x5d, 0x4f, + 0xfe, 0xb2, 0xa4, 0x6f, 0x9d, 0x64, 0x38, 0x33, 0xc3, 0xfc, 0x7b, 0x0b, 0x8c, 0x26, 0x84, 0x07, + 0x23, 0x86, 0x7e, 0x97, 0x7f, 0xa9, 0x58, 0x9b, 0x4f, 0x66, 0xbf, 0x41, 0x52, 0x03, 0xa3, 0xf9, + 0xe6, 0xf1, 0x02, 0x2c, 0x09, 0xd9, 0x97, 0x21, 0x33, 0xfc, 0x4c, 0x67, 0xf9, 0x92, 0xfd, 0x09, + 0x98, 0x60, 0x35, 0x0f, 0x29, 0xbb, 0xb1, 0x13, 0x52, 0xf1, 0x8c, 0x08, 0xbe, 0xf6, 0xff, 0x6d, + 0x01, 0x5a, 0xf1, 0x6b, 0xee, 0xc6, 0xae, 0x20, 0xce, 0xbf, 0xff, 0x5d, 0x28, 0xf1, 0xc7, 0x71, + 0x32, 0xca, 0xed, 0x7c, 0xdd, 0x09, 0x43, 0x4d, 0x22, 0xff, 0xa4, 0x68, 0xb7, 0xb4, 0xd6, 0x1e, + 0x1d, 0x77, 0xa2, 0x87, 0xde, 0x4a, 0x04, 0x1d, 0xfc, 0x68, 0x2a, 0xe8, 0xe0, 0x93, 0x99, 0x76, + 0x31, 0xe9, 0xde, 0xcb, 0x60, 0x84, 0xf6, 0x17, 0x2d, 0x18, 0x5b, 0x4d, 0x44, 0x6d, 0x3d, 0xcf, + 0x8c, 0x04, 0x32, 0x34, 0x4d, 0x15, 0x56, 0x8a, 0x05, 0xf4, 0x81, 0x4b, 0x62, 0xff, 0xd5, 0x82, + 0x38, 0xdc, 0xd5, 0x11, 0xb0, 0xdc, 0xf3, 0x06, 0xcb, 0x9d, 0xf9, 0x7c, 0x51, 0xdd, 0xc9, 0xe3, + 0xb8, 0xd1, 0x35, 0x35, 0x27, 0x6d, 0x5e, 0x2e, 0x31, 0x19, 0xbe, 0xcf, 0x46, 0xcd, 0x89, 0x53, + 0xb3, 0xf1, 0xcd, 0x02, 0x20, 0x85, 0xdb, 0x75, 0xa0, 0xca, 0x74, 0x8d, 0x07, 0x13, 0xa8, 0x72, + 0x07, 0x10, 0x33, 0x73, 0x09, 0x1c, 0x2f, 0xe4, 0x64, 0x5d, 0x21, 0x7b, 0x3e, 0x9c, 0x0d, 0xcd, + 0xb4, 0xf4, 0x5c, 0xbd, 0x9e, 0xa2, 0x86, 0x33, 0x5a, 0xd0, 0xcc, 0x97, 0x7a, 0xbb, 0x35, 0x5f, + 0xea, 0xeb, 0xe0, 0x82, 0xfd, 0x35, 0x0b, 0x46, 0xd4, 0x30, 0xbd, 0x4f, 0x5c, 0x40, 0x54, 0x7f, + 0x72, 0xee, 0x95, 0xb2, 0xd6, 0x65, 0xc6, 0x0c, 0x7c, 0x1f, 0x73, 0xa5, 0x77, 0xea, 0xee, 0x3d, + 0xa2, 0xe2, 0x29, 0x97, 0x84, 0x6b, 0xbc, 0x28, 0x3d, 0xd8, 0x2b, 0x8d, 0xa8, 0x7f, 0x3c, 0x82, + 0x6b, 0x5c, 0xc5, 0xfe, 0x25, 0xba, 0xd9, 0xcd, 0xa5, 0x88, 0x5e, 0x82, 0xde, 0xe6, 0x96, 0x13, + 0x92, 0x84, 0xab, 0x5c, 0x6f, 0x99, 0x16, 0x1e, 0xec, 0x95, 0x46, 0x55, 0x05, 0x56, 0x82, 0x39, + 0x76, 0xf7, 0xe1, 0x3f, 0xd3, 0x8b, 0xb3, 0x63, 0xf8, 0xcf, 0x7f, 0xb2, 0xa0, 0x67, 0x95, 0xde, + 0x5e, 0x0f, 0xff, 0x08, 0x78, 0xdd, 0x38, 0x02, 0x4e, 0xe7, 0x65, 0x16, 0xca, 0xdd, 0xfd, 0x4b, + 0x89, 0xdd, 0x7f, 0x36, 0x97, 0x42, 0xfb, 0x8d, 0xdf, 0x80, 0x21, 0x96, 0xaf, 0x48, 0xb8, 0x05, + 0xbe, 0x60, 0x6c, 0xf8, 0x52, 0x62, 0xc3, 0x8f, 0x69, 0xa8, 0xda, 0x4e, 0x7f, 0x0a, 0xfa, 0x85, + 0x9f, 0x59, 0x32, 0x22, 0x81, 0xc0, 0xc5, 0x12, 0x6e, 0xff, 0x7c, 0x11, 0x8c, 0xfc, 0x48, 0xe8, + 0xf7, 0x2d, 0x98, 0x09, 0xb8, 0xfd, 0x79, 0x6d, 0xa1, 0x15, 0xb8, 0xde, 0x66, 0xa5, 0xba, 0x45, + 0x6a, 0xad, 0xba, 0xeb, 0x6d, 0x2e, 0x6f, 0x7a, 0xbe, 0x2a, 0x5e, 0xbc, 0x4b, 0xaa, 0x2d, 0xa6, + 0x1b, 0xee, 0x90, 0x8c, 0x49, 0xf9, 0x71, 0x3c, 0xbf, 0xbf, 0x57, 0x9a, 0xc1, 0x87, 0xa2, 0x8d, + 0x0f, 0xd9, 0x17, 0xf4, 0xe7, 0x16, 0x5c, 0xe4, 0x79, 0x7a, 0xba, 0xef, 0x7f, 0x1b, 0x09, 0x47, + 0x59, 0x92, 0x8a, 0x89, 0xac, 0x91, 0xa0, 0x31, 0xf7, 0xb2, 0x18, 0xd0, 0x8b, 0xe5, 0xc3, 0xb5, + 0x85, 0x0f, 0xdb, 0x39, 0xfb, 0x7f, 0x2e, 0xc2, 0x88, 0x08, 0x13, 0x29, 0xee, 0x80, 0x97, 0x8c, + 0x25, 0xf1, 0x68, 0x62, 0x49, 0x4c, 0x18, 0xc8, 0x0f, 0xe6, 0xf8, 0x0f, 0x61, 0x82, 0x1e, 0xce, + 0x57, 0x89, 0x13, 0x44, 0xb7, 0x89, 0xc3, 0xad, 0x12, 0x8b, 0x87, 0x3e, 0xfd, 0x95, 0x78, 0xfc, + 0x7a, 0x92, 0x18, 0x4e, 0xd3, 0xff, 0x5e, 0xba, 0x73, 0x3c, 0x18, 0x4f, 0x45, 0xfa, 0x7c, 0x1b, + 0x06, 0x95, 0x93, 0x94, 0x38, 0x74, 0xda, 0x07, 0xcc, 0x4d, 0x52, 0xe0, 0x42, 0xcf, 0xd8, 0x41, + 0x2f, 0x26, 0x67, 0xff, 0x66, 0xc1, 0x68, 0x90, 0x4f, 0xe2, 0x2a, 0x0c, 0x38, 0x21, 0x0b, 0xe2, + 0x5d, 0x6b, 0x27, 0x97, 0x4e, 0x35, 0xc3, 0x1c, 0xd5, 0x66, 0x45, 0x4d, 0xac, 0x68, 0xa0, 0xab, + 0xdc, 0xf6, 0x73, 0x87, 0xb4, 0x13, 0x4a, 0xa7, 0xa8, 0x81, 0xb4, 0x0e, 0xdd, 0x21, 0x58, 0xd4, + 0x47, 0x9f, 0xe2, 0xc6, 0xb9, 0xd7, 0x3c, 0xff, 0x8e, 0x77, 0xc5, 0xf7, 0x65, 0x48, 0xa0, 0xee, + 0x08, 0x4e, 0x48, 0x93, 0x5c, 0x55, 0x1d, 0x9b, 0xd4, 0xba, 0x0b, 0x9d, 0xfd, 0x39, 0x60, 0x79, + 0x49, 0xcc, 0x98, 0x04, 0x21, 0x22, 0x30, 0x26, 0x62, 0x90, 0xca, 0x32, 0x31, 0x76, 0x99, 0xcf, + 0x6f, 0xb3, 0x76, 0xac, 0xc7, 0xb9, 0x66, 0x92, 0xc0, 0x49, 0x9a, 0xf6, 0x16, 0x3f, 0x84, 0x97, + 0x88, 0x13, 0xb5, 0x02, 0x12, 0xa2, 0x8f, 0xc3, 0x54, 0xfa, 0x65, 0x2c, 0xd4, 0x21, 0x16, 0xe3, + 0x9e, 0x4f, 0xef, 0xef, 0x95, 0xa6, 0x2a, 0x39, 0x38, 0x38, 0xb7, 0xb6, 0xfd, 0x2b, 0x16, 0x30, + 0x4f, 0xf0, 0x23, 0xe0, 0x7c, 0x3e, 0x66, 0x72, 0x3e, 0x53, 0x79, 0xd3, 0x99, 0xc3, 0xf4, 0xbc, + 0xc8, 0xd7, 0x70, 0x39, 0xf0, 0xef, 0xee, 0x0a, 0xdb, 0xad, 0xce, 0xcf, 0x38, 0xfb, 0xcb, 0x16, + 0xb0, 0x24, 0x3e, 0x98, 0xbf, 0xda, 0xa5, 0x82, 0xa3, 0xb3, 0x59, 0xc2, 0xc7, 0x61, 0x60, 0x43, + 0x0c, 0x7f, 0x86, 0xd0, 0xc9, 0xe8, 0xb0, 0x49, 0x5b, 0x4e, 0x9a, 0xf0, 0xe8, 0x14, 0xff, 0xb0, + 0xa2, 0x66, 0xff, 0xf7, 0x16, 0x4c, 0xe7, 0x57, 0x43, 0xeb, 0x70, 0x32, 0x20, 0xd5, 0x56, 0x10, + 0xd2, 0x2d, 0x21, 0x1e, 0x40, 0xc2, 0x29, 0x8a, 0x4f, 0xf5, 0x23, 0xfb, 0x7b, 0xa5, 0x93, 0x38, + 0x1b, 0x05, 0xe7, 0xd5, 0x45, 0xaf, 0xc0, 0x68, 0x2b, 0xe4, 0x9c, 0x1f, 0x63, 0xba, 0x42, 0x11, + 0x29, 0x9a, 0xf9, 0x0d, 0xad, 0x1b, 0x10, 0x9c, 0xc0, 0xb4, 0x7f, 0x80, 0x2f, 0x47, 0x15, 0x2c, + 0xba, 0x01, 0x13, 0x9e, 0xf6, 0x9f, 0xde, 0x80, 0xf2, 0xa9, 0xff, 0x78, 0xa7, 0x5b, 0x9f, 0x5d, + 0x97, 0x9a, 0xaf, 0x7a, 0x82, 0x0c, 0x4e, 0x53, 0xb6, 0x7f, 0xc1, 0x82, 0x93, 0x3a, 0xa2, 0xe6, + 0x0e, 0xd7, 0x49, 0x97, 0xb7, 0x00, 0x03, 0x7e, 0x93, 0x04, 0x4e, 0xe4, 0x07, 0xe2, 0x9a, 0xbb, + 0x20, 0x57, 0xe8, 0x0d, 0x51, 0x7e, 0x20, 0x92, 0xd7, 0x48, 0xea, 0xb2, 0x1c, 0xab, 0x9a, 0xc8, + 0x86, 0x3e, 0x26, 0x40, 0x0c, 0x85, 0xe3, 0x23, 0x3b, 0xb4, 0x98, 0x7d, 0x4a, 0x88, 0x05, 0xc4, + 0xfe, 0x7b, 0x8b, 0xaf, 0x4f, 0xbd, 0xeb, 0xe8, 0x5d, 0x18, 0x6f, 0x38, 0x51, 0x75, 0x6b, 0xf1, + 0x6e, 0x33, 0xe0, 0x2a, 0x5a, 0x39, 0x4e, 0xcf, 0x74, 0x1a, 0x27, 0xed, 0x23, 0x63, 0x03, 0xe9, + 0x95, 0x04, 0x31, 0x9c, 0x22, 0x8f, 0x6e, 0xc3, 0x10, 0x2b, 0x63, 0x3e, 0xbd, 0x61, 0x3b, 0x5e, + 0x26, 0xaf, 0x35, 0x65, 0xe2, 0xb3, 0x12, 0xd3, 0xc1, 0x3a, 0x51, 0xfb, 0xab, 0x45, 0x7e, 0x68, + 0xb0, 0xb7, 0xc7, 0x53, 0xd0, 0xdf, 0xf4, 0x6b, 0xf3, 0xcb, 0x0b, 0x58, 0xcc, 0x82, 0xba, 0xf7, + 0xca, 0xbc, 0x18, 0x4b, 0x38, 0xba, 0x00, 0x03, 0xe2, 0xa7, 0x54, 0xa9, 0xb3, 0x3d, 0x22, 0xf0, + 0x42, 0xac, 0xa0, 0xe8, 0x79, 0x80, 0x66, 0xe0, 0xef, 0xb8, 0x35, 0x16, 0x89, 0xa9, 0x68, 0x5a, + 0xe7, 0x95, 0x15, 0x04, 0x6b, 0x58, 0xe8, 0x55, 0x18, 0x69, 0x79, 0x21, 0xe7, 0x9f, 0xb4, 0x78, + 0xf7, 0xca, 0x6e, 0x6c, 0x5d, 0x07, 0x62, 0x13, 0x17, 0xcd, 0x42, 0x5f, 0xe4, 0x30, 0x6b, 0xb3, + 0xde, 0x7c, 0x23, 0xfa, 0x35, 0x8a, 0xa1, 0x67, 0x96, 0xa3, 0x15, 0xb0, 0xa8, 0x88, 0xde, 0x96, + 0xee, 0xf5, 0xfc, 0x26, 0x12, 0xde, 0x2b, 0xdd, 0xdd, 0x5a, 0x9a, 0x73, 0xbd, 0xf0, 0x8a, 0x31, + 0x68, 0xa1, 0x57, 0x00, 0xc8, 0xdd, 0x88, 0x04, 0x9e, 0x53, 0x57, 0x36, 0xa2, 0x8a, 0x91, 0x59, + 0xf0, 0x57, 0xfd, 0x68, 0x3d, 0x24, 0x8b, 0x0a, 0x03, 0x6b, 0xd8, 0xf6, 0x8f, 0x0e, 0x01, 0xc4, + 0x0f, 0x0d, 0x74, 0x0f, 0x06, 0xaa, 0x4e, 0xd3, 0xa9, 0xf2, 0xb4, 0xa9, 0xc5, 0x3c, 0xaf, 0xe7, + 0xb8, 0xc6, 0xcc, 0xbc, 0x40, 0xe7, 0xca, 0x1b, 0x19, 0x32, 0x7c, 0x40, 0x16, 0x77, 0x54, 0xd8, + 0xa8, 0xf6, 0xd0, 0x17, 0x2c, 0x18, 0x12, 0x91, 0x8e, 0xd8, 0x0c, 0x15, 0xf2, 0xf5, 0x6d, 0x5a, + 0xfb, 0xb3, 0x71, 0x0d, 0xde, 0x85, 0x17, 0xe4, 0x0a, 0xd5, 0x20, 0x1d, 0x7b, 0xa1, 0x37, 0x8c, + 0x3e, 0x2c, 0xdf, 0xb6, 0x45, 0x63, 0x28, 0xd5, 0xdb, 0x76, 0x90, 0x5d, 0x35, 0xfa, 0xb3, 0x76, + 0xdd, 0x78, 0xd6, 0xf6, 0xe4, 0xfb, 0x0f, 0x1b, 0xfc, 0x76, 0xa7, 0x17, 0x2d, 0x2a, 0xeb, 0xb1, + 0x44, 0x7a, 0xf3, 0x9d, 0x5e, 0xb5, 0x87, 0x5d, 0x87, 0x38, 0x22, 0x9f, 0x85, 0xb1, 0x9a, 0xc9, + 0xb5, 0x88, 0x95, 0xf8, 0x64, 0x1e, 0xdd, 0x04, 0x93, 0x13, 0xf3, 0x29, 0x09, 0x00, 0x4e, 0x12, + 0x46, 0x65, 0x1e, 0x5a, 0x66, 0xd9, 0xdb, 0xf0, 0x85, 0x07, 0x95, 0x9d, 0x3b, 0x97, 0xbb, 0x61, + 0x44, 0x1a, 0x14, 0x33, 0x66, 0x12, 0x56, 0x45, 0x5d, 0xac, 0xa8, 0xa0, 0x37, 0xa1, 0x8f, 0x79, + 0x3d, 0x86, 0x53, 0x03, 0xf9, 0x6a, 0x0d, 0x33, 0x12, 0x6a, 0xbc, 0x21, 0xd9, 0xdf, 0x10, 0x0b, + 0x0a, 0xe8, 0xaa, 0xf4, 0x29, 0x0e, 0x97, 0xbd, 0xf5, 0x90, 0x30, 0x9f, 0xe2, 0xc1, 0xb9, 0xc7, + 0x63, 0x77, 0x61, 0x5e, 0x9e, 0x99, 0x7f, 0xd6, 0xa8, 0x49, 0xd9, 0x3e, 0xf1, 0x5f, 0xa6, 0xb5, + 0x15, 0x71, 0xdb, 0x32, 0xbb, 0x67, 0xa6, 0xbe, 0x8d, 0x87, 0xf3, 0xa6, 0x49, 0x02, 0x27, 0x69, + 0x52, 0x16, 0x9a, 0xef, 0x7a, 0xe1, 0x83, 0xd5, 0xe9, 0xec, 0xe0, 0x92, 0x03, 0x76, 0x1b, 0xf1, + 0x12, 0x2c, 0xea, 0x23, 0x17, 0xc6, 0x02, 0x83, 0xbd, 0x90, 0xe1, 0xd6, 0xce, 0x77, 0xc7, 0xc4, + 0x68, 0x81, 0xfc, 0x4d, 0x32, 0x38, 0x49, 0x17, 0xbd, 0xa9, 0x31, 0x4a, 0x23, 0xed, 0x5f, 0xfe, + 0x9d, 0x58, 0xa3, 0xe9, 0x6d, 0x18, 0x31, 0x0e, 0x9b, 0x87, 0xaa, 0x82, 0xf4, 0x60, 0x3c, 0x79, + 0xb2, 0x3c, 0x54, 0xcd, 0xe3, 0xdf, 0xf6, 0xc0, 0xa8, 0xb9, 0x13, 0xd0, 0x45, 0x18, 0x14, 0x44, + 0x54, 0x46, 0x2b, 0xb5, 0xb9, 0x57, 0x24, 0x00, 0xc7, 0x38, 0x2c, 0x91, 0x19, 0xab, 0xae, 0xf9, + 0x0a, 0xc4, 0x89, 0xcc, 0x14, 0x04, 0x6b, 0x58, 0xf4, 0x01, 0x7b, 0xdb, 0xf7, 0x23, 0x75, 0x8f, + 0xaa, 0xed, 0x32, 0xc7, 0x4a, 0xb1, 0x80, 0xd2, 0xfb, 0x73, 0x9b, 0x04, 0x1e, 0xa9, 0x9b, 0x29, + 0x1d, 0xd4, 0xfd, 0x79, 0x4d, 0x07, 0x62, 0x13, 0x97, 0x72, 0x01, 0x7e, 0xc8, 0xf6, 0x9f, 0x78, + 0x26, 0xc7, 0xbe, 0x17, 0x15, 0x1e, 0x45, 0x42, 0xc2, 0xd1, 0x27, 0xe0, 0xa4, 0x0a, 0x9f, 0x28, + 0x56, 0x97, 0x6c, 0xb1, 0xcf, 0x90, 0x6a, 0x9d, 0x9c, 0xcf, 0x46, 0xc3, 0x79, 0xf5, 0xd1, 0xeb, + 0x30, 0x2a, 0x9e, 0x52, 0x92, 0x62, 0xbf, 0x69, 0x48, 0x78, 0xcd, 0x80, 0xe2, 0x04, 0xb6, 0x4c, + 0x4a, 0xc1, 0xde, 0x18, 0x92, 0xc2, 0x40, 0x3a, 0x29, 0x85, 0x0e, 0xc7, 0xa9, 0x1a, 0x68, 0x16, + 0xc6, 0x38, 0xeb, 0xe8, 0x7a, 0x9b, 0x7c, 0x4e, 0x84, 0x67, 0xa7, 0xda, 0x54, 0x37, 0x4c, 0x30, + 0x4e, 0xe2, 0xa3, 0xcb, 0x30, 0xec, 0x04, 0xd5, 0x2d, 0x37, 0x22, 0x55, 0xba, 0x33, 0x98, 0x2d, + 0x9f, 0x66, 0x89, 0x39, 0xab, 0xc1, 0xb0, 0x81, 0x69, 0xdf, 0x83, 0xc9, 0x8c, 0xf0, 0x32, 0x74, + 0xe1, 0x38, 0x4d, 0x57, 0x7e, 0x53, 0xc2, 0xdd, 0x61, 0xb6, 0xbc, 0x2c, 0xbf, 0x46, 0xc3, 0xa2, + 0xab, 0x93, 0x85, 0xa1, 0xd1, 0x92, 0x6f, 0xab, 0xd5, 0xb9, 0x24, 0x01, 0x38, 0xc6, 0xb1, 0xff, + 0xb9, 0x00, 0x63, 0x19, 0x0a, 0x3a, 0x96, 0x00, 0x3a, 0xf1, 0xd2, 0x8a, 0xf3, 0x3d, 0x9b, 0x39, + 0x4e, 0x0a, 0x87, 0xc8, 0x71, 0x52, 0xec, 0x94, 0xe3, 0xa4, 0xe7, 0xbd, 0xe4, 0x38, 0x31, 0x47, + 0xac, 0xb7, 0xab, 0x11, 0xcb, 0xc8, 0x8b, 0xd2, 0x77, 0xc8, 0xbc, 0x28, 0xc6, 0xa0, 0xf7, 0x77, + 0x31, 0xe8, 0x3f, 0x5d, 0x80, 0xf1, 0xa4, 0x6e, 0xef, 0x08, 0xe4, 0xe3, 0x6f, 0x1a, 0xf2, 0xf1, + 0x0b, 0xdd, 0x78, 0xe2, 0xe7, 0xca, 0xca, 0x71, 0x42, 0x56, 0xfe, 0x74, 0x57, 0xd4, 0xda, 0xcb, + 0xcd, 0x7f, 0xb1, 0x00, 0xc7, 0x33, 0x55, 0x9e, 0x47, 0x30, 0x36, 0x37, 0x8c, 0xb1, 0x79, 0xae, + 0xeb, 0x28, 0x05, 0xb9, 0x03, 0x74, 0x2b, 0x31, 0x40, 0x17, 0xbb, 0x27, 0xd9, 0x7e, 0x94, 0xbe, + 0x55, 0x84, 0xb3, 0x99, 0xf5, 0x62, 0xf1, 0xf2, 0x92, 0x21, 0x5e, 0x7e, 0x3e, 0x21, 0x5e, 0xb6, + 0xdb, 0xd7, 0x7e, 0x30, 0xf2, 0x66, 0xe1, 0xad, 0xcf, 0x62, 0x8e, 0xdc, 0xa7, 0xac, 0xd9, 0xf0, + 0xd6, 0x57, 0x84, 0xb0, 0x49, 0xf7, 0x7b, 0x49, 0xc6, 0xfc, 0x67, 0x16, 0x9c, 0xca, 0x9c, 0x9b, + 0x23, 0x90, 0xf4, 0xad, 0x9a, 0x92, 0xbe, 0xa7, 0xba, 0x5e, 0xad, 0x39, 0xa2, 0xbf, 0x2f, 0xf6, + 0xe5, 0x7c, 0x0b, 0x13, 0x40, 0xdc, 0x80, 0x21, 0xa7, 0x5a, 0x25, 0x61, 0xb8, 0xe2, 0xd7, 0x54, + 0x3a, 0x84, 0xe7, 0xd8, 0xf3, 0x30, 0x2e, 0x3e, 0xd8, 0x2b, 0x4d, 0x27, 0x49, 0xc4, 0x60, 0xac, + 0x53, 0x40, 0x9f, 0x82, 0x81, 0x50, 0x66, 0xb2, 0xec, 0xb9, 0xff, 0x4c, 0x96, 0x8c, 0xc9, 0x55, + 0x02, 0x16, 0x45, 0x12, 0x7d, 0xbf, 0x1e, 0xfd, 0xa9, 0x8d, 0x68, 0x91, 0x77, 0xf2, 0x3e, 0x62, + 0x40, 0x3d, 0x0f, 0xb0, 0xa3, 0x5e, 0x32, 0x49, 0xe1, 0x89, 0xf6, 0xc6, 0xd1, 0xb0, 0xd0, 0x1b, + 0x30, 0x1e, 0xf2, 0xc0, 0xa7, 0xb1, 0x91, 0x0a, 0x5f, 0x8b, 0x2c, 0x76, 0x5c, 0x25, 0x01, 0xc3, + 0x29, 0x6c, 0xb4, 0x24, 0x5b, 0x65, 0xe6, 0x48, 0x7c, 0x79, 0x9e, 0x8f, 0x5b, 0x14, 0x26, 0x49, + 0xc7, 0x92, 0x93, 0xc0, 0x86, 0x5f, 0xab, 0x89, 0x3e, 0x05, 0x40, 0x17, 0x91, 0x10, 0xa2, 0xf4, + 0xe7, 0x1f, 0xa1, 0xf4, 0x6c, 0xa9, 0x65, 0x7a, 0x32, 0x30, 0x37, 0xfb, 0x05, 0x45, 0x04, 0x6b, + 0x04, 0x91, 0x03, 0x23, 0xf1, 0xbf, 0x38, 0x47, 0xfb, 0x85, 0xdc, 0x16, 0x92, 0xc4, 0x99, 0x82, + 0x61, 0x41, 0x27, 0x81, 0x4d, 0x8a, 0xe8, 0x93, 0x70, 0x6a, 0x27, 0xd7, 0xf2, 0x87, 0x73, 0x82, + 0x2c, 0xe9, 0x7a, 0xbe, 0xbd, 0x4f, 0x7e, 0x7d, 0xfb, 0x7f, 0x07, 0x78, 0xa4, 0xcd, 0x49, 0x8f, + 0x66, 0x4d, 0xad, 0xfd, 0x33, 0x49, 0xc9, 0xc6, 0x74, 0x66, 0x65, 0x43, 0xd4, 0x91, 0xd8, 0x50, + 0x85, 0xf7, 0xbc, 0xa1, 0x7e, 0xc2, 0xd2, 0x64, 0x4e, 0xdc, 0xa6, 0xfb, 0x63, 0x87, 0xbc, 0xc1, + 0x1e, 0xa0, 0x10, 0x6a, 0x23, 0x43, 0x92, 0xf3, 0x7c, 0xd7, 0xdd, 0xe9, 0x5e, 0xb4, 0xf3, 0xf5, + 0xec, 0x80, 0xef, 0x5c, 0xc8, 0x73, 0xe5, 0xb0, 0xdf, 0x7f, 0x54, 0xc1, 0xdf, 0xbf, 0x69, 0xc1, + 0xa9, 0x54, 0x31, 0xef, 0x03, 0x09, 0x45, 0xb4, 0xbb, 0xd5, 0xf7, 0xdc, 0x79, 0x49, 0x90, 0x7f, + 0xc3, 0x55, 0xf1, 0x0d, 0xa7, 0x72, 0xf1, 0x92, 0x5d, 0xff, 0xd2, 0xdf, 0x94, 0x26, 0x59, 0x03, + 0x26, 0x22, 0xce, 0xef, 0x3a, 0x6a, 0xc2, 0xb9, 0x6a, 0x2b, 0x08, 0xe2, 0xc5, 0x9a, 0xb1, 0x39, + 0xf9, 0x5b, 0xef, 0xf1, 0xfd, 0xbd, 0xd2, 0xb9, 0xf9, 0x0e, 0xb8, 0xb8, 0x23, 0x35, 0xe4, 0x01, + 0x6a, 0xa4, 0xec, 0xeb, 0xd8, 0x01, 0x90, 0x23, 0x87, 0x49, 0x5b, 0xe3, 0x71, 0x4b, 0xd9, 0x0c, + 0x2b, 0xbd, 0x0c, 0xca, 0x47, 0x2b, 0x3d, 0xf9, 0xce, 0xc4, 0xa5, 0x9f, 0xbe, 0x0e, 0x67, 0xdb, + 0x2f, 0xa6, 0x43, 0x85, 0x72, 0xf8, 0x4b, 0x0b, 0xce, 0xb4, 0x8d, 0x17, 0xf6, 0x5d, 0xf8, 0x58, + 0xb0, 0x3f, 0x6f, 0xc1, 0xa3, 0x99, 0x35, 0x92, 0x4e, 0x78, 0x55, 0x5a, 0xa8, 0x99, 0xa3, 0xc6, + 0x91, 0x73, 0x24, 0x00, 0xc7, 0x38, 0x86, 0xc5, 0x66, 0xa1, 0xa3, 0xc5, 0xe6, 0x1f, 0x59, 0x90, + 0xba, 0xea, 0x8f, 0x80, 0xf3, 0x5c, 0x36, 0x39, 0xcf, 0xc7, 0xbb, 0x19, 0xcd, 0x1c, 0xa6, 0xf3, + 0x1f, 0xc7, 0xe0, 0x44, 0x8e, 0x27, 0xf6, 0x0e, 0x4c, 0x6c, 0x56, 0x89, 0x19, 0x7a, 0xa3, 0x5d, + 0x48, 0xba, 0xb6, 0x71, 0x3a, 0xe6, 0x8e, 0xef, 0xef, 0x95, 0x26, 0x52, 0x28, 0x38, 0xdd, 0x04, + 0xfa, 0xbc, 0x05, 0xc7, 0x9c, 0x3b, 0xe1, 0x22, 0x7d, 0x41, 0xb8, 0xd5, 0xb9, 0xba, 0x5f, 0xdd, + 0xa6, 0x8c, 0x99, 0xdc, 0x56, 0x2f, 0x66, 0x0a, 0xa3, 0x6f, 0x55, 0x52, 0xf8, 0x46, 0xf3, 0x53, + 0xfb, 0x7b, 0xa5, 0x63, 0x59, 0x58, 0x38, 0xb3, 0x2d, 0x84, 0x45, 0xc6, 0x2f, 0x27, 0xda, 0x6a, + 0x17, 0x1c, 0x26, 0xcb, 0x65, 0x9e, 0xb3, 0xc4, 0x12, 0x82, 0x15, 0x1d, 0xf4, 0x19, 0x18, 0xdc, + 0x94, 0x71, 0x20, 0x32, 0x58, 0xee, 0x78, 0x20, 0xdb, 0x47, 0xc7, 0xe0, 0x26, 0x30, 0x0a, 0x09, + 0xc7, 0x44, 0xd1, 0xeb, 0x50, 0xf4, 0x36, 0x42, 0x11, 0xa2, 0x2e, 0xdb, 0x12, 0xd7, 0xb4, 0x75, + 0xe6, 0x21, 0x98, 0x56, 0x97, 0x2a, 0x98, 0x56, 0x44, 0x57, 0xa1, 0x18, 0xdc, 0xae, 0x09, 0x4d, + 0x4a, 0xe6, 0x26, 0xc5, 0x73, 0x0b, 0x39, 0xbd, 0x62, 0x94, 0xf0, 0xdc, 0x02, 0xa6, 0x24, 0x50, + 0x19, 0x7a, 0x99, 0xfb, 0xb2, 0x60, 0x6d, 0x33, 0x9f, 0xf2, 0x6d, 0xc2, 0x00, 0x70, 0x8f, 0x44, + 0x86, 0x80, 0x39, 0x21, 0xb4, 0x06, 0x7d, 0x55, 0xd7, 0xab, 0x91, 0x40, 0xf0, 0xb2, 0x1f, 0xce, + 0xd4, 0x99, 0x30, 0x8c, 0x1c, 0x9a, 0x5c, 0x85, 0xc0, 0x30, 0xb0, 0xa0, 0xc5, 0xa8, 0x92, 0xe6, + 0xd6, 0x86, 0xbc, 0xb1, 0xb2, 0xa9, 0x92, 0xe6, 0xd6, 0x52, 0xa5, 0x2d, 0x55, 0x86, 0x81, 0x05, + 0x2d, 0xf4, 0x0a, 0x14, 0x36, 0xaa, 0xc2, 0x35, 0x39, 0x53, 0x79, 0x62, 0x46, 0xd1, 0x9a, 0xeb, + 0xdb, 0xdf, 0x2b, 0x15, 0x96, 0xe6, 0x71, 0x61, 0xa3, 0x8a, 0x56, 0xa1, 0x7f, 0x83, 0xc7, 0xdd, + 0x11, 0xfa, 0x91, 0x27, 0xb3, 0x43, 0x02, 0xa5, 0x42, 0xf3, 0x70, 0xef, 0x52, 0x01, 0xc0, 0x92, + 0x08, 0x4b, 0x40, 0xa5, 0xe2, 0x07, 0x89, 0xf0, 0xa5, 0x33, 0x87, 0x8b, 0xf9, 0xc4, 0x9f, 0x1a, + 0x71, 0x14, 0x22, 0xac, 0x51, 0xa4, 0xab, 0xda, 0xb9, 0xd7, 0x0a, 0x58, 0x6e, 0x0b, 0xa1, 0x1a, + 0xc9, 0x5c, 0xd5, 0xb3, 0x12, 0xa9, 0xdd, 0xaa, 0x56, 0x48, 0x38, 0x26, 0x8a, 0xb6, 0x61, 0x64, + 0x27, 0x6c, 0x6e, 0x11, 0xb9, 0xa5, 0x59, 0xd8, 0xbb, 0x1c, 0x6e, 0xf6, 0xa6, 0x40, 0x74, 0x83, + 0xa8, 0xe5, 0xd4, 0x53, 0xa7, 0x10, 0x7b, 0xd6, 0xdc, 0xd4, 0x89, 0x61, 0x93, 0x36, 0x1d, 0xfe, + 0x77, 0x5b, 0xfe, 0xed, 0xdd, 0x88, 0x88, 0xa8, 0xa3, 0x99, 0xc3, 0xff, 0x16, 0x47, 0x49, 0x0f, + 0xbf, 0x00, 0x60, 0x49, 0x04, 0xdd, 0x14, 0xc3, 0xc3, 0x4e, 0xcf, 0xf1, 0xfc, 0x90, 0xe6, 0xb3, + 0x12, 0x29, 0x67, 0x50, 0xd8, 0x69, 0x19, 0x93, 0x62, 0xa7, 0x64, 0x73, 0xcb, 0x8f, 0x7c, 0x2f, + 0x71, 0x42, 0x4f, 0xe4, 0x9f, 0x92, 0xe5, 0x0c, 0xfc, 0xf4, 0x29, 0x99, 0x85, 0x85, 0x33, 0xdb, + 0x42, 0x35, 0x18, 0x6d, 0xfa, 0x41, 0x74, 0xc7, 0x0f, 0xe4, 0xfa, 0x42, 0x6d, 0x04, 0xa5, 0x06, + 0xa6, 0x68, 0x91, 0x19, 0xe6, 0x98, 0x10, 0x9c, 0xa0, 0x89, 0x3e, 0x0e, 0xfd, 0x61, 0xd5, 0xa9, + 0x93, 0xe5, 0x1b, 0x53, 0x93, 0xf9, 0xd7, 0x4f, 0x85, 0xa3, 0xe4, 0xac, 0x2e, 0x1e, 0x36, 0x89, + 0xa3, 0x60, 0x49, 0x0e, 0x2d, 0x41, 0x2f, 0x4b, 0xec, 0xcc, 0x42, 0xe4, 0xe6, 0x44, 0x66, 0x4f, + 0xb9, 0xd5, 0xf0, 0xb3, 0x89, 0x15, 0x63, 0x5e, 0x9d, 0xee, 0x01, 0x21, 0x29, 0xf0, 0xc3, 0xa9, + 0xe3, 0xf9, 0x7b, 0x40, 0x08, 0x18, 0x6e, 0x54, 0xda, 0xed, 0x01, 0x85, 0x84, 0x63, 0xa2, 0xf4, + 0x64, 0xa6, 0xa7, 0xe9, 0x89, 0x36, 0x26, 0x93, 0xb9, 0x67, 0x29, 0x3b, 0x99, 0xe9, 0x49, 0x4a, + 0x49, 0xd8, 0x7f, 0x30, 0x90, 0xe6, 0x59, 0x98, 0x84, 0xe9, 0x3f, 0xb7, 0x52, 0x36, 0x13, 0x1f, + 0xe9, 0x56, 0xe0, 0xfd, 0x00, 0x1f, 0xae, 0x9f, 0xb7, 0xe0, 0x44, 0x33, 0xf3, 0x43, 0x04, 0x03, + 0xd0, 0x9d, 0xdc, 0x9c, 0x7f, 0xba, 0x0a, 0xa7, 0x9c, 0x0d, 0xc7, 0x39, 0x2d, 0x25, 0x85, 0x03, + 0xc5, 0xf7, 0x2c, 0x1c, 0x58, 0x81, 0x81, 0x2a, 0x7f, 0xc9, 0xc9, 0x34, 0x00, 0x5d, 0x05, 0x03, + 0x65, 0xac, 0x84, 0x78, 0x02, 0x6e, 0x60, 0x45, 0x02, 0xfd, 0xa4, 0x05, 0x67, 0x92, 0x5d, 0xc7, + 0x84, 0x81, 0x85, 0xc1, 0x24, 0x17, 0x6b, 0x2d, 0x89, 0xef, 0x4f, 0xf1, 0xff, 0x06, 0xf2, 0x41, + 0x27, 0x04, 0xdc, 0xbe, 0x31, 0xb4, 0x90, 0x21, 0x57, 0xeb, 0x33, 0x35, 0x8a, 0x5d, 0xc8, 0xd6, + 0x5e, 0x84, 0xe1, 0x86, 0xdf, 0xf2, 0x22, 0x61, 0xf7, 0x28, 0x8c, 0xa7, 0x98, 0xd1, 0xd0, 0x8a, + 0x56, 0x8e, 0x0d, 0xac, 0x84, 0x44, 0x6e, 0xe0, 0xbe, 0x25, 0x72, 0xef, 0xc0, 0xb0, 0xa7, 0xb9, + 0x04, 0xb4, 0x7b, 0xc1, 0x0a, 0xe9, 0xa2, 0x86, 0xcd, 0x7b, 0xa9, 0x97, 0x60, 0x83, 0x5a, 0x7b, + 0x69, 0x19, 0xbc, 0x37, 0x69, 0xd9, 0x91, 0x3e, 0x89, 0xed, 0x5f, 0x2f, 0x64, 0xbc, 0x18, 0xb8, + 0x54, 0xee, 0x35, 0x53, 0x2a, 0x77, 0x3e, 0x29, 0x95, 0x4b, 0xa9, 0xaa, 0x0c, 0x81, 0x5c, 0xf7, + 0x19, 0x25, 0xbb, 0x0e, 0xf0, 0xfc, 0xc3, 0x16, 0x9c, 0x64, 0xba, 0x0f, 0xda, 0xc0, 0x7b, 0xd6, + 0x77, 0x30, 0x93, 0xd4, 0xeb, 0xd9, 0xe4, 0x70, 0x5e, 0x3b, 0x76, 0x1d, 0xce, 0x75, 0xba, 0x77, + 0x99, 0x85, 0x6f, 0x4d, 0x19, 0x47, 0xc4, 0x16, 0xbe, 0xb5, 0xe5, 0x05, 0xcc, 0x20, 0xdd, 0x86, + 0x2f, 0xb4, 0xff, 0x7f, 0x0b, 0x8a, 0x65, 0xbf, 0x76, 0x04, 0x2f, 0xfa, 0x8f, 0x19, 0x2f, 0xfa, + 0x47, 0xb2, 0x6f, 0xfc, 0x5a, 0xae, 0xb2, 0x6f, 0x31, 0xa1, 0xec, 0x3b, 0x93, 0x47, 0xa0, 0xbd, + 0x6a, 0xef, 0x97, 0x8a, 0x30, 0x54, 0xf6, 0x6b, 0x6a, 0x9f, 0xfd, 0xaf, 0xf7, 0xe3, 0xc8, 0x93, + 0x9b, 0x7d, 0x4a, 0xa3, 0xcc, 0x2c, 0x7a, 0x65, 0xdc, 0x89, 0xef, 0x32, 0x7f, 0x9e, 0x5b, 0xc4, + 0xdd, 0xdc, 0x8a, 0x48, 0x2d, 0xf9, 0x39, 0x47, 0xe7, 0xcf, 0xf3, 0xed, 0x22, 0x8c, 0x25, 0x5a, + 0x47, 0x75, 0x18, 0xa9, 0xeb, 0xaa, 0x24, 0xb1, 0x4e, 0xef, 0x4b, 0x0b, 0x25, 0xfc, 0x21, 0xb4, + 0x22, 0x6c, 0x12, 0x47, 0x33, 0x00, 0x9e, 0x6e, 0x15, 0xae, 0x02, 0x15, 0x6b, 0x16, 0xe1, 0x1a, + 0x06, 0x7a, 0x09, 0x86, 0x22, 0xbf, 0xe9, 0xd7, 0xfd, 0xcd, 0xdd, 0x6b, 0x44, 0x46, 0xb6, 0x54, + 0x46, 0xc3, 0x6b, 0x31, 0x08, 0xeb, 0x78, 0xe8, 0x2e, 0x4c, 0x28, 0x22, 0x95, 0x07, 0xa0, 0x5e, + 0x63, 0x62, 0x93, 0xd5, 0x24, 0x45, 0x9c, 0x6e, 0x04, 0xbd, 0x02, 0xa3, 0xcc, 0x7a, 0x99, 0xd5, + 0xbf, 0x46, 0x76, 0x65, 0xc4, 0x63, 0xc6, 0x61, 0xaf, 0x18, 0x10, 0x9c, 0xc0, 0x44, 0xf3, 0x30, + 0xd1, 0x70, 0xc3, 0x44, 0xf5, 0x3e, 0x56, 0x9d, 0x75, 0x60, 0x25, 0x09, 0xc4, 0x69, 0x7c, 0xfb, + 0x57, 0xc5, 0x1c, 0x7b, 0x91, 0xfb, 0xc1, 0x76, 0x7c, 0x7f, 0x6f, 0xc7, 0x6f, 0x59, 0x30, 0x4e, + 0x5b, 0x67, 0x26, 0x99, 0x92, 0x91, 0x52, 0x39, 0x31, 0xac, 0x36, 0x39, 0x31, 0xce, 0xd3, 0x63, + 0xbb, 0xe6, 0xb7, 0x22, 0x21, 0x1d, 0xd5, 0xce, 0x65, 0x5a, 0x8a, 0x05, 0x54, 0xe0, 0x91, 0x20, + 0x10, 0x7e, 0xef, 0x3a, 0x1e, 0x09, 0x02, 0x2c, 0xa0, 0x32, 0x65, 0x46, 0x4f, 0x76, 0xca, 0x0c, + 0x1e, 0xf9, 0x5c, 0x58, 0xc1, 0x09, 0x96, 0x56, 0x8b, 0x7c, 0x2e, 0xcd, 0xe3, 0x62, 0x1c, 0xfb, + 0xeb, 0x45, 0x18, 0x2e, 0xfb, 0xb5, 0xd8, 0xb0, 0xe3, 0x45, 0xc3, 0xb0, 0xe3, 0x5c, 0xc2, 0xb0, + 0x63, 0x5c, 0xc7, 0xfd, 0xc0, 0x8c, 0xe3, 0x3b, 0x65, 0xc6, 0xf1, 0x87, 0x16, 0x9b, 0xb5, 0x85, + 0xd5, 0x0a, 0xb7, 0xf0, 0x45, 0x97, 0x60, 0x88, 0x9d, 0x70, 0x2c, 0xd0, 0x82, 0xb4, 0x76, 0x60, + 0x29, 0x2c, 0x57, 0xe3, 0x62, 0xac, 0xe3, 0xa0, 0x0b, 0x30, 0x10, 0x12, 0x27, 0xa8, 0x6e, 0xa9, + 0xe3, 0x5d, 0x98, 0x26, 0xf0, 0x32, 0xac, 0xa0, 0xe8, 0xad, 0x38, 0xe8, 0x76, 0x31, 0xdf, 0x5c, + 0x58, 0xef, 0x0f, 0xdf, 0x22, 0xf9, 0x91, 0xb6, 0xed, 0x5b, 0x80, 0xd2, 0xf8, 0x5d, 0xf8, 0x5f, + 0x95, 0xcc, 0xb0, 0xb0, 0x83, 0xa9, 0x90, 0xb0, 0xff, 0x62, 0xc1, 0x68, 0xd9, 0xaf, 0xd1, 0xad, + 0xfb, 0xbd, 0xb4, 0x4f, 0xf5, 0x8c, 0x03, 0x7d, 0x6d, 0x32, 0x0e, 0x3c, 0x06, 0xbd, 0x65, 0xbf, + 0xd6, 0x21, 0x74, 0xed, 0x7f, 0x63, 0x41, 0x7f, 0xd9, 0xaf, 0x1d, 0x81, 0xe2, 0xe5, 0x35, 0x53, + 0xf1, 0x72, 0x32, 0x67, 0xdd, 0xe4, 0xe8, 0x5a, 0xfe, 0xa4, 0x07, 0x46, 0x68, 0x3f, 0xfd, 0x4d, + 0x39, 0x95, 0xc6, 0xb0, 0x59, 0x5d, 0x0c, 0x1b, 0x7d, 0x06, 0xf8, 0xf5, 0xba, 0x7f, 0x27, 0x39, + 0xad, 0x4b, 0xac, 0x14, 0x0b, 0x28, 0x7a, 0x16, 0x06, 0x9a, 0x01, 0xd9, 0x71, 0x7d, 0xc1, 0x5f, + 0x6b, 0x6a, 0xac, 0xb2, 0x28, 0xc7, 0x0a, 0x83, 0x3e, 0xbc, 0x43, 0xd7, 0xa3, 0xbc, 0x44, 0xd5, + 0xf7, 0x6a, 0x5c, 0x37, 0x51, 0x14, 0x69, 0xb1, 0xb4, 0x72, 0x6c, 0x60, 0xa1, 0x5b, 0x30, 0xc8, + 0xfe, 0xb3, 0x63, 0xa7, 0xf7, 0xd0, 0xc7, 0x8e, 0x48, 0x14, 0x2c, 0x08, 0xe0, 0x98, 0x16, 0x7a, + 0x1e, 0x20, 0x92, 0xa9, 0x65, 0x42, 0x11, 0xc2, 0x54, 0xbd, 0x45, 0x54, 0xd2, 0x99, 0x10, 0x6b, + 0x58, 0xe8, 0x19, 0x18, 0x8c, 0x1c, 0xb7, 0x7e, 0xdd, 0xf5, 0x98, 0xfe, 0x9e, 0xf6, 0x5f, 0xe4, + 0xeb, 0x15, 0x85, 0x38, 0x86, 0x53, 0x5e, 0x90, 0xc5, 0x84, 0x9a, 0xdb, 0x8d, 0x44, 0x6a, 0xba, + 0x22, 0xe7, 0x05, 0xaf, 0xab, 0x52, 0xac, 0x61, 0xa0, 0x2d, 0x38, 0xed, 0x7a, 0x2c, 0x85, 0x14, + 0xa9, 0x6c, 0xbb, 0xcd, 0xb5, 0xeb, 0x95, 0x9b, 0x24, 0x70, 0x37, 0x76, 0xe7, 0x9c, 0xea, 0x36, + 0xf1, 0x64, 0x42, 0xfc, 0xc7, 0x45, 0x17, 0x4f, 0x2f, 0xb7, 0xc1, 0xc5, 0x6d, 0x29, 0x21, 0x9b, + 0x6e, 0xc7, 0x80, 0x38, 0x0d, 0x21, 0x13, 0xe0, 0xe9, 0x67, 0x58, 0x09, 0x16, 0x10, 0xfb, 0x05, + 0xb6, 0x27, 0x6e, 0x54, 0xd0, 0xd3, 0xc6, 0xf1, 0x72, 0x42, 0x3f, 0x5e, 0x0e, 0xf6, 0x4a, 0x7d, + 0x37, 0x2a, 0x5a, 0x7c, 0xa0, 0xcb, 0x70, 0xbc, 0xec, 0xd7, 0xca, 0x7e, 0x10, 0x2d, 0xf9, 0xc1, + 0x1d, 0x27, 0xa8, 0xc9, 0x25, 0x58, 0x92, 0x11, 0x92, 0xe8, 0x19, 0xdb, 0xcb, 0x4f, 0x20, 0x23, + 0xfa, 0xd1, 0x0b, 0x8c, 0xab, 0x3b, 0xa4, 0x43, 0x6a, 0x95, 0xf1, 0x17, 0x2a, 0x51, 0xdb, 0x15, + 0x27, 0x22, 0xe8, 0x06, 0x8c, 0x54, 0xf5, 0xab, 0x56, 0x54, 0x7f, 0x4a, 0x5e, 0x76, 0xc6, 0x3d, + 0x9c, 0x79, 0x37, 0x9b, 0xf5, 0xed, 0x6f, 0x5a, 0xa2, 0x15, 0x2e, 0xad, 0xe0, 0x76, 0xaf, 0x9d, + 0xcf, 0xdc, 0x79, 0x98, 0x08, 0xf4, 0x2a, 0x9a, 0xfd, 0xd8, 0x71, 0x9e, 0xf9, 0x26, 0x01, 0xc4, + 0x69, 0x7c, 0xf4, 0x49, 0x38, 0x65, 0x14, 0x4a, 0x55, 0xba, 0x96, 0x7f, 0x9a, 0xc9, 0x73, 0x70, + 0x1e, 0x12, 0xce, 0xaf, 0x6f, 0xff, 0x20, 0x9c, 0x48, 0x7e, 0x97, 0x90, 0xb0, 0xdc, 0xe7, 0xd7, + 0x15, 0x0e, 0xf7, 0x75, 0xf6, 0x4b, 0x30, 0x41, 0x9f, 0xde, 0x8a, 0x8d, 0x64, 0xf3, 0xd7, 0x39, + 0x08, 0xd5, 0x6f, 0x0e, 0xb0, 0x6b, 0x30, 0x91, 0x7d, 0x0d, 0x7d, 0x1a, 0x46, 0x43, 0xc2, 0x22, + 0xaf, 0x49, 0xc9, 0x5e, 0x1b, 0x6f, 0xf2, 0xca, 0xa2, 0x8e, 0xc9, 0x5f, 0x2f, 0x66, 0x19, 0x4e, + 0x50, 0x43, 0x0d, 0x18, 0xbd, 0xe3, 0x7a, 0x35, 0xff, 0x4e, 0x28, 0xe9, 0x0f, 0xe4, 0xab, 0x09, + 0x6e, 0x71, 0xcc, 0x44, 0x1f, 0x8d, 0xe6, 0x6e, 0x19, 0xc4, 0x70, 0x82, 0x38, 0x3d, 0x6a, 0x82, + 0x96, 0x37, 0x1b, 0xae, 0x87, 0x24, 0x10, 0x71, 0xe1, 0xd8, 0x51, 0x83, 0x65, 0x21, 0x8e, 0xe1, + 0xf4, 0xa8, 0x61, 0x7f, 0x98, 0x3b, 0x3a, 0x3b, 0xcb, 0xc4, 0x51, 0x83, 0x55, 0x29, 0xd6, 0x30, + 0xe8, 0x51, 0xcc, 0xfe, 0xad, 0xfa, 0x1e, 0xf6, 0xfd, 0x48, 0x1e, 0xde, 0x2c, 0x55, 0xa5, 0x56, + 0x8e, 0x0d, 0xac, 0x9c, 0x28, 0x74, 0x3d, 0x87, 0x8d, 0x42, 0x87, 0xa2, 0x36, 0x1e, 0xf8, 0x3c, + 0x1a, 0xf2, 0xe5, 0x76, 0x1e, 0xf8, 0x07, 0xf7, 0xe5, 0x9d, 0x4f, 0x79, 0x81, 0x0d, 0x31, 0x40, + 0xbd, 0x3c, 0xcc, 0x1e, 0x53, 0x64, 0x56, 0xf8, 0xe8, 0x48, 0x18, 0x5a, 0x84, 0xfe, 0x70, 0x37, + 0xac, 0x46, 0xf5, 0xb0, 0x5d, 0x3a, 0xd2, 0x0a, 0x43, 0xd1, 0xb2, 0x61, 0xf3, 0x2a, 0x58, 0xd6, + 0x45, 0x55, 0x98, 0x14, 0x14, 0xe7, 0xb7, 0x1c, 0x4f, 0x25, 0x49, 0xe4, 0x16, 0x8b, 0x97, 0xf6, + 0xf7, 0x4a, 0x93, 0xa2, 0x65, 0x1d, 0x7c, 0xb0, 0x57, 0xa2, 0x5b, 0x32, 0x03, 0x82, 0xb3, 0xa8, + 0xf1, 0x25, 0x5f, 0xad, 0xfa, 0x8d, 0x66, 0x39, 0xf0, 0x37, 0xdc, 0x3a, 0x69, 0xa7, 0x0c, 0xae, + 0x18, 0x98, 0x62, 0xc9, 0x1b, 0x65, 0x38, 0x41, 0x0d, 0xdd, 0x86, 0x31, 0xa7, 0xd9, 0x9c, 0x0d, + 0x1a, 0x7e, 0x20, 0x1b, 0x18, 0xca, 0xd7, 0x2a, 0xcc, 0x9a, 0xa8, 0x3c, 0x47, 0x62, 0xa2, 0x10, + 0x27, 0x09, 0xd2, 0x81, 0x12, 0x1b, 0xcd, 0x18, 0xa8, 0x91, 0x78, 0xa0, 0xc4, 0xbe, 0xcc, 0x18, + 0xa8, 0x0c, 0x08, 0xce, 0xa2, 0x66, 0xff, 0x00, 0x63, 0xfc, 0x2b, 0xee, 0xa6, 0xc7, 0x9c, 0xe3, + 0x50, 0x03, 0x46, 0x9a, 0xec, 0xd8, 0x17, 0xf9, 0xcb, 0xc4, 0x51, 0xf1, 0x62, 0x97, 0xc2, 0xcb, + 0x3b, 0x2c, 0x03, 0xab, 0x61, 0xc4, 0x5a, 0xd6, 0xc9, 0x61, 0x93, 0xba, 0xfd, 0x8b, 0xd3, 0x8c, + 0x75, 0xac, 0x70, 0x89, 0x64, 0xbf, 0x70, 0x55, 0x14, 0x32, 0x88, 0xe9, 0x7c, 0xd9, 0x7f, 0xbc, + 0xbe, 0x84, 0xbb, 0x23, 0x96, 0x75, 0xd1, 0xa7, 0x60, 0x94, 0x3e, 0xe9, 0x15, 0xfb, 0x16, 0x4e, + 0x1d, 0xcb, 0x8f, 0x81, 0xa5, 0xb0, 0xf4, 0xdc, 0x86, 0x7a, 0x65, 0x9c, 0x20, 0x86, 0xde, 0x62, + 0x76, 0x9d, 0x92, 0x74, 0xa1, 0x1b, 0xd2, 0xba, 0x09, 0xa7, 0x24, 0xab, 0x11, 0x41, 0x2d, 0x98, + 0x4c, 0x67, 0x70, 0x0e, 0xa7, 0xec, 0xfc, 0xb7, 0x51, 0x3a, 0x09, 0x73, 0x9c, 0x84, 0x2e, 0x0d, + 0x0b, 0x71, 0x16, 0x7d, 0x74, 0x3d, 0x99, 0x5f, 0xb7, 0x68, 0x68, 0x0d, 0x52, 0x39, 0x76, 0x47, + 0xda, 0xa6, 0xd6, 0xdd, 0x84, 0x33, 0x5a, 0x8a, 0xd2, 0x2b, 0x81, 0xc3, 0xec, 0x8a, 0x5c, 0x76, + 0x1b, 0x69, 0x4c, 0xed, 0xa3, 0xfb, 0x7b, 0xa5, 0x33, 0x6b, 0xed, 0x10, 0x71, 0x7b, 0x3a, 0xe8, + 0x06, 0x1c, 0xe7, 0x11, 0x5c, 0x16, 0x88, 0x53, 0xab, 0xbb, 0x9e, 0xe2, 0x9a, 0xf9, 0xd9, 0x75, + 0x6a, 0x7f, 0xaf, 0x74, 0x7c, 0x36, 0x0b, 0x01, 0x67, 0xd7, 0x43, 0xaf, 0xc1, 0x60, 0xcd, 0x93, + 0xa7, 0x6c, 0x9f, 0x91, 0x05, 0x76, 0x70, 0x61, 0xb5, 0xa2, 0xbe, 0x3f, 0xfe, 0x83, 0xe3, 0x0a, + 0x68, 0x93, 0xab, 0xad, 0x94, 0xac, 0xb1, 0x3f, 0x15, 0xd8, 0x33, 0x29, 0x8e, 0x37, 0x42, 0x22, + 0x70, 0x7d, 0xad, 0x72, 0xb9, 0x33, 0xa2, 0x25, 0x18, 0x84, 0xd1, 0x9b, 0x80, 0x44, 0xb6, 0xa1, + 0xd9, 0x2a, 0x4b, 0x8e, 0xa7, 0xd9, 0x92, 0x2a, 0x11, 0x42, 0x25, 0x85, 0x81, 0x33, 0x6a, 0xa1, + 0xab, 0xf4, 0x78, 0xd4, 0x4b, 0xc5, 0xf1, 0xab, 0x72, 0x8d, 0x2f, 0x90, 0x66, 0x40, 0x98, 0xf9, + 0xa3, 0x49, 0x11, 0x27, 0xea, 0xa1, 0x1a, 0x9c, 0x76, 0x5a, 0x91, 0xcf, 0x34, 0x82, 0x26, 0xea, + 0x9a, 0xbf, 0x4d, 0x3c, 0xa6, 0x8c, 0x1f, 0x60, 0x01, 0x43, 0x4f, 0xcf, 0xb6, 0xc1, 0xc3, 0x6d, + 0xa9, 0xd0, 0xe7, 0x14, 0x1d, 0x0b, 0x4d, 0x59, 0x67, 0x78, 0x77, 0x73, 0x0d, 0xb6, 0xc4, 0x40, + 0x2f, 0xc1, 0xd0, 0x96, 0x1f, 0x46, 0xab, 0x24, 0xba, 0xe3, 0x07, 0xdb, 0x22, 0xbd, 0x41, 0x9c, + 0x52, 0x26, 0x06, 0x61, 0x1d, 0x0f, 0x3d, 0x05, 0xfd, 0xcc, 0x54, 0x6c, 0x79, 0x81, 0xdd, 0xb5, + 0x03, 0xf1, 0x19, 0x73, 0x95, 0x17, 0x63, 0x09, 0x97, 0xa8, 0xcb, 0xe5, 0x79, 0x76, 0x1c, 0x27, + 0x50, 0x97, 0xcb, 0xf3, 0x58, 0xc2, 0xe9, 0x72, 0x0d, 0xb7, 0x9c, 0x80, 0x94, 0x03, 0xbf, 0x4a, + 0x42, 0x2d, 0x91, 0xd1, 0x23, 0x3c, 0x79, 0x03, 0x5d, 0xae, 0x95, 0x2c, 0x04, 0x9c, 0x5d, 0x0f, + 0x91, 0x74, 0x7a, 0xde, 0xd1, 0x7c, 0x55, 0x69, 0x9a, 0x1d, 0xec, 0x32, 0x43, 0xaf, 0x07, 0xe3, + 0x2a, 0x31, 0x30, 0x4f, 0xd7, 0x10, 0x4e, 0x8d, 0xb1, 0xb5, 0xdd, 0x7d, 0xae, 0x07, 0xa5, 0x7c, + 0x5e, 0x4e, 0x50, 0xc2, 0x29, 0xda, 0x46, 0x44, 0xda, 0xf1, 0x8e, 0x11, 0x69, 0x2f, 0xc2, 0x60, + 0xd8, 0xba, 0x5d, 0xf3, 0x1b, 0x8e, 0xeb, 0x31, 0x8b, 0x1b, 0xed, 0xe1, 0x5e, 0x91, 0x00, 0x1c, + 0xe3, 0xa0, 0x25, 0x18, 0x70, 0xa4, 0x66, 0x19, 0xe5, 0x07, 0xdb, 0x53, 0xfa, 0x64, 0x1e, 0x7f, + 0x4a, 0xea, 0x92, 0x55, 0x5d, 0xf4, 0x2a, 0x8c, 0x88, 0x80, 0x1e, 0x22, 0x97, 0xfe, 0xa4, 0xe9, + 0xbe, 0x5c, 0xd1, 0x81, 0xd8, 0xc4, 0x45, 0xeb, 0x30, 0x14, 0xf9, 0x75, 0xe6, 0x83, 0x4b, 0xb9, + 0xe4, 0x13, 0xf9, 0x31, 0x71, 0xd7, 0x14, 0x9a, 0xae, 0xf3, 0x50, 0x55, 0xb1, 0x4e, 0x07, 0xad, + 0xf1, 0xf5, 0xce, 0xd2, 0x16, 0x91, 0x50, 0x24, 0x63, 0x3f, 0x93, 0x67, 0x2e, 0xc9, 0xd0, 0xcc, + 0xed, 0x20, 0x6a, 0x62, 0x9d, 0x0c, 0xba, 0x02, 0x13, 0xcd, 0xc0, 0xf5, 0xd9, 0x9a, 0x50, 0x9a, + 0xf2, 0x29, 0x33, 0x49, 0x69, 0x39, 0x89, 0x80, 0xd3, 0x75, 0x58, 0x3c, 0x16, 0x51, 0x38, 0x75, + 0x8a, 0x27, 0x5a, 0xe3, 0x72, 0x10, 0x5e, 0x86, 0x15, 0x14, 0xad, 0xb0, 0x93, 0x98, 0x8b, 0xf0, + 0xa6, 0xa6, 0xf3, 0xbd, 0xfc, 0x75, 0x51, 0x1f, 0xe7, 0xfd, 0xd5, 0x5f, 0x1c, 0x53, 0x40, 0x35, + 0x2d, 0xbf, 0x39, 0x7d, 0x41, 0x85, 0x53, 0xa7, 0xdb, 0xd8, 0xeb, 0x26, 0x9e, 0xcb, 0x31, 0x43, + 0x60, 0x14, 0x87, 0x38, 0x41, 0x13, 0xbd, 0x01, 0xe3, 0x22, 0x58, 0x41, 0x3c, 0x4c, 0x67, 0x62, + 0x9f, 0x26, 0x9c, 0x80, 0xe1, 0x14, 0x36, 0x4f, 0x74, 0xe6, 0xdc, 0xae, 0x13, 0x71, 0xf4, 0x5d, + 0x77, 0xbd, 0xed, 0x70, 0xea, 0x2c, 0x3b, 0x1f, 0x44, 0xa2, 0xb3, 0x24, 0x14, 0x67, 0xd4, 0x40, + 0x6b, 0x30, 0xde, 0x0c, 0x08, 0x69, 0xb0, 0x77, 0x92, 0xb8, 0xcf, 0x4a, 0x3c, 0x1c, 0x11, 0xed, + 0x49, 0x39, 0x01, 0x3b, 0xc8, 0x28, 0xc3, 0x29, 0x0a, 0xe8, 0x0e, 0x0c, 0xf8, 0x3b, 0x24, 0xd8, + 0x22, 0x4e, 0x6d, 0xea, 0x5c, 0x1b, 0x4f, 0x3b, 0x71, 0xb9, 0xdd, 0x10, 0xb8, 0x09, 0x43, 0x24, + 0x59, 0xdc, 0xd9, 0x10, 0x49, 0x36, 0x86, 0xfe, 0x0b, 0x0b, 0x4e, 0x49, 0xd5, 0x5e, 0xa5, 0x49, + 0x47, 0x7d, 0xde, 0xf7, 0xc2, 0x28, 0xe0, 0x01, 0x74, 0x1e, 0xcd, 0x0f, 0x2a, 0xb3, 0x96, 0x53, + 0x49, 0x69, 0x11, 0x4e, 0xe5, 0x61, 0x84, 0x38, 0xbf, 0x45, 0xfa, 0xb2, 0x0f, 0x49, 0x24, 0x0f, + 0xa3, 0xd9, 0x70, 0xe9, 0xad, 0x85, 0xd5, 0xa9, 0xc7, 0x78, 0xf4, 0x1f, 0xba, 0x19, 0x2a, 0x49, + 0x20, 0x4e, 0xe3, 0xa3, 0x4b, 0x50, 0xf0, 0xc3, 0xa9, 0xc7, 0xdb, 0xa4, 0xc4, 0xf7, 0x6b, 0x37, + 0x2a, 0xdc, 0x20, 0xf5, 0x46, 0x05, 0x17, 0xfc, 0x50, 0x26, 0x1b, 0xa3, 0xcf, 0xd9, 0x70, 0xea, + 0x09, 0x2e, 0x73, 0x96, 0xc9, 0xc6, 0x58, 0x21, 0x8e, 0xe1, 0x68, 0x0b, 0xc6, 0x42, 0x43, 0x6c, + 0x10, 0x4e, 0x9d, 0x67, 0x23, 0xf5, 0x44, 0xde, 0xa4, 0x19, 0xd8, 0x5a, 0x16, 0x20, 0x93, 0x0a, + 0x4e, 0x92, 0xe5, 0xbb, 0x4b, 0x13, 0x5c, 0x84, 0x53, 0x4f, 0x76, 0xd8, 0x5d, 0x1a, 0xb2, 0xbe, + 0xbb, 0x74, 0x1a, 0x38, 0x41, 0x13, 0xad, 0xeb, 0x6e, 0x8c, 0x17, 0xf2, 0x8d, 0x1b, 0x33, 0x1d, + 0x18, 0x47, 0xf2, 0x9c, 0x17, 0xa7, 0xbf, 0x0f, 0x26, 0x52, 0x5c, 0xd8, 0x61, 0x7c, 0x3a, 0xa6, + 0xb7, 0x61, 0xc4, 0x58, 0xe9, 0x0f, 0xd5, 0xe4, 0xe7, 0xcf, 0x06, 0x61, 0x50, 0x99, 0x62, 0xa0, + 0x8b, 0xa6, 0x95, 0xcf, 0xa9, 0xa4, 0x95, 0xcf, 0x40, 0xd9, 0xaf, 0x19, 0x86, 0x3d, 0x6b, 0x19, + 0xb1, 0x72, 0xf3, 0xce, 0xd5, 0xee, 0x1d, 0xcf, 0x34, 0xf5, 0x52, 0xb1, 0x6b, 0x73, 0xa1, 0x9e, + 0xb6, 0x1a, 0xab, 0x2b, 0x30, 0xe1, 0xf9, 0x8c, 0xf5, 0x27, 0x35, 0xc9, 0xd7, 0x31, 0xf6, 0x6d, + 0x50, 0x8f, 0xe5, 0x96, 0x40, 0xc0, 0xe9, 0x3a, 0xb4, 0x41, 0xce, 0x7f, 0x25, 0x55, 0x64, 0x9c, + 0x3d, 0xc3, 0x02, 0x4a, 0x9f, 0x9c, 0xfc, 0x57, 0x38, 0x35, 0x9e, 0xff, 0xe4, 0xe4, 0x95, 0x92, + 0x3c, 0x5e, 0x28, 0x79, 0x3c, 0xa6, 0x11, 0x6a, 0xfa, 0xb5, 0xe5, 0xb2, 0x78, 0x3d, 0x68, 0x51, + 0xec, 0x6b, 0xcb, 0x65, 0xcc, 0x61, 0x68, 0x16, 0xfa, 0xd8, 0x0f, 0x19, 0x23, 0x27, 0x6f, 0xf7, + 0x2f, 0x97, 0xb5, 0x1c, 0xaa, 0xac, 0x02, 0x16, 0x15, 0x99, 0xc4, 0x9f, 0x3e, 0xb9, 0x98, 0xc4, + 0xbf, 0xff, 0x3e, 0x25, 0xfe, 0x92, 0x00, 0x8e, 0x69, 0xa1, 0xbb, 0x70, 0xdc, 0x78, 0xe6, 0x2a, + 0x4f, 0x3c, 0xc8, 0x37, 0x06, 0x48, 0x20, 0xcf, 0x9d, 0x11, 0x9d, 0x3e, 0xbe, 0x9c, 0x45, 0x09, + 0x67, 0x37, 0x80, 0xea, 0x30, 0x51, 0x4d, 0xb5, 0x3a, 0xd0, 0x7d, 0xab, 0x6a, 0x5d, 0xa4, 0x5b, + 0x4c, 0x13, 0x46, 0xaf, 0xc2, 0xc0, 0xbb, 0x3e, 0x37, 0xdc, 0x13, 0x2f, 0x1e, 0x19, 0x05, 0x66, + 0xe0, 0xad, 0x1b, 0x15, 0x56, 0x7e, 0xb0, 0x57, 0x1a, 0x2a, 0xfb, 0x35, 0xf9, 0x17, 0xab, 0x0a, + 0xe8, 0xc7, 0x2c, 0x98, 0x4e, 0xbf, 0xa3, 0x55, 0xa7, 0x47, 0xba, 0xef, 0xb4, 0x2d, 0x1a, 0x9d, + 0x5e, 0xcc, 0x25, 0x87, 0xdb, 0x34, 0x85, 0x3e, 0x4a, 0xf7, 0x53, 0xe8, 0xde, 0x23, 0x22, 0x01, + 0xfd, 0xa3, 0xf1, 0x7e, 0xa2, 0xa5, 0x07, 0x7b, 0xa5, 0x31, 0x7e, 0xe0, 0xba, 0xf7, 0x54, 0xbc, + 0x7d, 0x5e, 0x01, 0xfd, 0x20, 0x1c, 0x0f, 0xd2, 0x72, 0x6d, 0x22, 0x79, 0xfb, 0xa7, 0xbb, 0x39, + 0xbc, 0x93, 0x13, 0x8e, 0xb3, 0x08, 0xe2, 0xec, 0x76, 0xec, 0xdf, 0xb3, 0x98, 0x3e, 0x43, 0x74, + 0x8b, 0x84, 0xad, 0x7a, 0x74, 0x04, 0xc6, 0x72, 0x8b, 0x86, 0x3d, 0xc1, 0x7d, 0x5b, 0xbb, 0xfd, + 0x2f, 0x16, 0xb3, 0x76, 0x3b, 0x42, 0xbf, 0xbd, 0xb7, 0x60, 0x20, 0x12, 0xad, 0x89, 0xae, 0xe7, + 0x59, 0xe6, 0xc8, 0x4e, 0x31, 0x8b, 0x3f, 0xf5, 0x76, 0x92, 0xa5, 0x58, 0x91, 0xb1, 0xff, 0x47, + 0x3e, 0x03, 0x12, 0x72, 0x04, 0x6a, 0xdb, 0x05, 0x53, 0x6d, 0x5b, 0xea, 0xf0, 0x05, 0x39, 0xea, + 0xdb, 0xff, 0xc1, 0xec, 0x37, 0x93, 0x19, 0xbe, 0xdf, 0xcd, 0x2c, 0xed, 0x2f, 0x5a, 0x00, 0x71, + 0x82, 0x93, 0x2e, 0x12, 0x4e, 0x5f, 0xa6, 0xaf, 0x25, 0x3f, 0xf2, 0xab, 0x7e, 0x5d, 0xa8, 0x8d, + 0x4e, 0xc7, 0x9a, 0x63, 0x5e, 0x7e, 0xa0, 0xfd, 0xc6, 0x0a, 0x1b, 0x95, 0x64, 0xc4, 0xe1, 0x62, + 0x6c, 0xcb, 0x60, 0x44, 0x1b, 0xfe, 0x8a, 0x05, 0xc7, 0xb2, 0x9c, 0x40, 0xe8, 0xdb, 0x9b, 0x4b, + 0x4f, 0x95, 0x09, 0xac, 0x9a, 0xcd, 0x9b, 0xa2, 0x1c, 0x2b, 0x8c, 0xae, 0x33, 0x79, 0x1f, 0x2e, + 0xf9, 0xc6, 0x0d, 0x18, 0x29, 0x07, 0x44, 0xe3, 0x2f, 0x5e, 0x8f, 0xf3, 0x02, 0x0d, 0xce, 0x3d, + 0x7b, 0xe8, 0xc8, 0x4a, 0xf6, 0x57, 0x0b, 0x70, 0x8c, 0x1b, 0x72, 0xcd, 0xee, 0xf8, 0x6e, 0xad, + 0xec, 0xd7, 0x84, 0xeb, 0xee, 0xdb, 0x30, 0xdc, 0xd4, 0x44, 0xde, 0xed, 0x02, 0xc9, 0xeb, 0xa2, + 0xf1, 0x58, 0x48, 0xa7, 0x97, 0x62, 0x83, 0x16, 0xaa, 0xc1, 0x30, 0xd9, 0x71, 0xab, 0xca, 0x1a, + 0xa8, 0x70, 0xe8, 0x4b, 0x5a, 0xb5, 0xb2, 0xa8, 0xd1, 0xc1, 0x06, 0xd5, 0xae, 0xcd, 0xaf, 0x35, + 0x16, 0xad, 0xa7, 0x83, 0x05, 0xd0, 0xcf, 0x5a, 0x70, 0x32, 0x27, 0xec, 0x3c, 0x6d, 0xee, 0x0e, + 0x33, 0x99, 0x13, 0xcb, 0x56, 0x35, 0xc7, 0x0d, 0xe9, 0xb0, 0x80, 0xa2, 0x8f, 0x03, 0x34, 0xe3, + 0x94, 0x9b, 0x1d, 0xe2, 0x73, 0x1b, 0x91, 0x7a, 0xb5, 0xa0, 0xab, 0x2a, 0x33, 0xa7, 0x46, 0xcb, + 0xfe, 0x4a, 0x0f, 0xf4, 0x32, 0xc3, 0x2b, 0x54, 0x86, 0xfe, 0x2d, 0x1e, 0x13, 0xb0, 0xed, 0xbc, + 0x51, 0x5c, 0x19, 0x64, 0x30, 0x9e, 0x37, 0xad, 0x14, 0x4b, 0x32, 0x68, 0x05, 0x26, 0x79, 0x3a, + 0xd1, 0xfa, 0x02, 0xa9, 0x3b, 0xbb, 0x52, 0x9a, 0x5c, 0x60, 0x9f, 0xaa, 0xa4, 0xea, 0xcb, 0x69, + 0x14, 0x9c, 0x55, 0x0f, 0xbd, 0x0e, 0xa3, 0xf4, 0x75, 0xef, 0xb7, 0x22, 0x49, 0x89, 0xe7, 0xef, + 0x54, 0x0f, 0x9e, 0x35, 0x03, 0x8a, 0x13, 0xd8, 0xe8, 0x55, 0x18, 0x69, 0xa6, 0xe4, 0xe6, 0xbd, + 0xb1, 0x80, 0xc9, 0x94, 0x95, 0x9b, 0xb8, 0xcc, 0x0f, 0xa4, 0xc5, 0xbc, 0x5e, 0xd6, 0xb6, 0x02, + 0x12, 0x6e, 0xf9, 0xf5, 0x1a, 0xe3, 0x80, 0x7b, 0x35, 0x3f, 0x90, 0x04, 0x1c, 0xa7, 0x6a, 0x50, + 0x2a, 0x1b, 0x8e, 0x5b, 0x6f, 0x05, 0x24, 0xa6, 0xd2, 0x67, 0x52, 0x59, 0x4a, 0xc0, 0x71, 0xaa, + 0x46, 0x67, 0x85, 0x40, 0xff, 0x83, 0x51, 0x08, 0xd8, 0xbf, 0x5c, 0x00, 0x63, 0x6a, 0xbf, 0x87, + 0xf3, 0x8a, 0xbe, 0x06, 0x3d, 0x9b, 0x41, 0xb3, 0x2a, 0x8c, 0x0c, 0x33, 0xbf, 0xec, 0x0a, 0x2e, + 0xcf, 0xeb, 0x5f, 0x46, 0xff, 0x63, 0x56, 0x8b, 0xee, 0xf1, 0xe3, 0xe5, 0xc0, 0xa7, 0x97, 0x9c, + 0x0c, 0x1b, 0xaa, 0xdc, 0xad, 0xfa, 0xe5, 0x1b, 0xbb, 0x4d, 0x80, 0x6d, 0xe1, 0x33, 0xc2, 0x29, + 0x18, 0xf6, 0x78, 0x15, 0xf1, 0xc2, 0x96, 0x54, 0xd0, 0x25, 0x18, 0x12, 0xa9, 0x1e, 0x99, 0x57, + 0x10, 0xdf, 0x4c, 0xcc, 0x7e, 0x70, 0x21, 0x2e, 0xc6, 0x3a, 0x8e, 0xfd, 0xe3, 0x05, 0x98, 0xcc, + 0x70, 0xeb, 0xe4, 0xd7, 0xc8, 0xa6, 0x1b, 0x46, 0xc1, 0x6e, 0xf2, 0x72, 0xc2, 0xa2, 0x1c, 0x2b, + 0x0c, 0x7a, 0x56, 0xf1, 0x8b, 0x2a, 0x79, 0x39, 0x09, 0xb7, 0x29, 0x01, 0x3d, 0xdc, 0xe5, 0x44, + 0xaf, 0xed, 0x56, 0x48, 0x64, 0x2c, 0x7f, 0x75, 0x6d, 0x33, 0x63, 0x03, 0x06, 0xa1, 0x4f, 0xc0, + 0x4d, 0xa5, 0x41, 0xd7, 0x9e, 0x80, 0x5c, 0x87, 0xce, 0x61, 0xb4, 0x73, 0x11, 0xf1, 0x1c, 0x2f, + 0x12, 0x0f, 0xc5, 0x38, 0xc6, 0x33, 0x2b, 0xc5, 0x02, 0x6a, 0x7f, 0xb9, 0x08, 0xa7, 0x72, 0x1d, + 0xbd, 0x69, 0xd7, 0x1b, 0xbe, 0xe7, 0x46, 0xbe, 0x32, 0xcc, 0xe4, 0x71, 0x9d, 0x49, 0x73, 0x6b, + 0x45, 0x94, 0x63, 0x85, 0x81, 0xce, 0x43, 0x2f, 0x93, 0xb5, 0x27, 0xd3, 0xbc, 0xe1, 0xb9, 0x05, + 0x1e, 0x31, 0x93, 0x83, 0xb5, 0x5b, 0xbd, 0xd8, 0xf6, 0x56, 0x7f, 0x8c, 0x72, 0x30, 0x7e, 0x3d, + 0x79, 0xa1, 0xd0, 0xee, 0xfa, 0x7e, 0x1d, 0x33, 0x20, 0x7a, 0x42, 0x8c, 0x57, 0xc2, 0x12, 0x11, + 0x3b, 0x35, 0x3f, 0xd4, 0x06, 0xed, 0x29, 0xe8, 0xdf, 0x26, 0xbb, 0x81, 0xeb, 0x6d, 0x26, 0x2d, + 0x54, 0xaf, 0xf1, 0x62, 0x2c, 0xe1, 0x66, 0x56, 0xf3, 0xfe, 0x07, 0x91, 0xd5, 0x5c, 0x5f, 0x01, + 0x03, 0x1d, 0xd9, 0x93, 0x9f, 0x28, 0xc2, 0x18, 0x9e, 0x5b, 0xf8, 0x60, 0x22, 0xd6, 0xd3, 0x13, + 0xf1, 0x20, 0x92, 0x7f, 0x1f, 0x6e, 0x36, 0x7e, 0xdb, 0x82, 0x31, 0x96, 0x70, 0x52, 0x44, 0x69, + 0x71, 0x7d, 0xef, 0x08, 0x9e, 0x02, 0x8f, 0x41, 0x6f, 0x40, 0x1b, 0x15, 0x33, 0xa8, 0xf6, 0x38, + 0xeb, 0x09, 0xe6, 0x30, 0x74, 0x1a, 0x7a, 0x58, 0x17, 0xe8, 0xe4, 0x0d, 0xf3, 0x23, 0x78, 0xc1, + 0x89, 0x1c, 0xcc, 0x4a, 0x59, 0xbc, 0x48, 0x4c, 0x9a, 0x75, 0x97, 0x77, 0x3a, 0xb6, 0x84, 0x78, + 0x7f, 0x84, 0x80, 0xc9, 0xec, 0xda, 0x7b, 0x8b, 0x17, 0x99, 0x4d, 0xb2, 0xfd, 0x33, 0xfb, 0x1f, + 0x0a, 0x70, 0x36, 0xb3, 0x5e, 0xd7, 0xf1, 0x22, 0xdb, 0xd7, 0x7e, 0x98, 0xe9, 0xe9, 0x8a, 0x47, + 0x68, 0xff, 0xdf, 0xd3, 0x2d, 0xf7, 0xdf, 0xdb, 0x45, 0x18, 0xc7, 0xcc, 0x21, 0x7b, 0x9f, 0x84, + 0x71, 0xcc, 0xec, 0x5b, 0x8e, 0x98, 0xe0, 0x5f, 0x0b, 0x39, 0xdf, 0xc2, 0x04, 0x06, 0x17, 0xe8, + 0x39, 0xc3, 0x80, 0xa1, 0x7c, 0x84, 0xf3, 0x33, 0x86, 0x97, 0x61, 0x05, 0x45, 0xb3, 0x30, 0xd6, + 0x70, 0x3d, 0x7a, 0xf8, 0xec, 0x9a, 0xac, 0xb8, 0x52, 0x91, 0xac, 0x98, 0x60, 0x9c, 0xc4, 0x47, + 0xae, 0x16, 0xe2, 0x91, 0x7f, 0xdd, 0xab, 0x87, 0xda, 0x75, 0x33, 0xa6, 0x95, 0x88, 0x1a, 0xc5, + 0x8c, 0x70, 0x8f, 0x2b, 0x9a, 0x9c, 0xa8, 0xd8, 0xbd, 0x9c, 0x68, 0x38, 0x5b, 0x46, 0x34, 0xfd, + 0x2a, 0x8c, 0xdc, 0xb7, 0x6e, 0xc4, 0xfe, 0x56, 0x11, 0x1e, 0x69, 0xb3, 0xed, 0xf9, 0x59, 0x6f, + 0xcc, 0x81, 0x76, 0xd6, 0xa7, 0xe6, 0xa1, 0x0c, 0xc7, 0x36, 0x5a, 0xf5, 0xfa, 0x2e, 0x73, 0x74, + 0x23, 0x35, 0x89, 0x21, 0x78, 0x4a, 0x29, 0x1c, 0x39, 0xb6, 0x94, 0x81, 0x83, 0x33, 0x6b, 0xd2, + 0x27, 0x16, 0xbd, 0x49, 0x76, 0x15, 0xa9, 0xc4, 0x13, 0x0b, 0xeb, 0x40, 0x6c, 0xe2, 0xa2, 0x2b, + 0x30, 0xe1, 0xec, 0x38, 0x2e, 0x4f, 0xef, 0x21, 0x09, 0xf0, 0x37, 0x96, 0x92, 0x45, 0xcf, 0x26, + 0x11, 0x70, 0xba, 0x0e, 0x7a, 0x13, 0x90, 0x7f, 0x9b, 0x39, 0xcf, 0xd4, 0xae, 0x10, 0x4f, 0x28, + 0xf3, 0xd9, 0xdc, 0x15, 0xe3, 0x23, 0xe1, 0x46, 0x0a, 0x03, 0x67, 0xd4, 0x4a, 0x04, 0x1b, 0xec, + 0xcb, 0x0f, 0x36, 0xd8, 0xfe, 0x5c, 0xec, 0x98, 0x19, 0xf1, 0x1d, 0x18, 0x39, 0xac, 0xb5, 0xf7, + 0x53, 0xd0, 0x1f, 0x88, 0x9c, 0xf3, 0x09, 0xaf, 0x72, 0x99, 0x91, 0x5b, 0xc2, 0xed, 0xff, 0xc7, + 0x02, 0x25, 0x4b, 0x36, 0xe3, 0x8a, 0xbf, 0xca, 0x4c, 0xd7, 0xb9, 0x14, 0x5c, 0x0b, 0x25, 0x76, + 0x5c, 0x33, 0x5d, 0x8f, 0x81, 0xd8, 0xc4, 0xe5, 0xcb, 0x2d, 0x8c, 0x23, 0x58, 0x18, 0x0f, 0x08, + 0xa1, 0x35, 0x54, 0x18, 0xe8, 0x13, 0xd0, 0x5f, 0x73, 0x77, 0xdc, 0x50, 0xc8, 0xd1, 0x0e, 0xad, + 0xb7, 0x8b, 0xbf, 0x6f, 0x81, 0x93, 0xc1, 0x92, 0x9e, 0xfd, 0x53, 0x16, 0x28, 0x75, 0xe7, 0x55, + 0xe2, 0xd4, 0xa3, 0x2d, 0xf4, 0x06, 0x80, 0xa4, 0xa0, 0x64, 0x6f, 0xd2, 0x08, 0x0b, 0xb0, 0x82, + 0x1c, 0x18, 0xff, 0xb0, 0x56, 0x07, 0xbd, 0x0e, 0x7d, 0x5b, 0x8c, 0x96, 0xf8, 0xb6, 0xf3, 0x4a, + 0xd5, 0xc5, 0x4a, 0x0f, 0xf6, 0x4a, 0xc7, 0xcc, 0x36, 0xe5, 0x2d, 0xc6, 0x6b, 0xd9, 0x3f, 0x51, + 0x88, 0xe7, 0xf4, 0xad, 0x96, 0x1f, 0x39, 0x47, 0xc0, 0x89, 0x5c, 0x31, 0x38, 0x91, 0x27, 0xda, + 0xe9, 0x73, 0x59, 0x97, 0x72, 0x39, 0x90, 0x1b, 0x09, 0x0e, 0xe4, 0xc9, 0xce, 0xa4, 0xda, 0x73, + 0x1e, 0xff, 0x93, 0x05, 0x13, 0x06, 0xfe, 0x11, 0x5c, 0x80, 0x4b, 0xe6, 0x05, 0xf8, 0x68, 0xc7, + 0x6f, 0xc8, 0xb9, 0xf8, 0x7e, 0xb4, 0x98, 0xe8, 0x3b, 0xbb, 0xf0, 0xde, 0x85, 0x9e, 0x2d, 0x27, + 0xa8, 0x89, 0x77, 0xfd, 0xc5, 0xae, 0xc6, 0x7a, 0xe6, 0xaa, 0x13, 0x08, 0x03, 0x8e, 0x67, 0xe5, + 0xa8, 0xd3, 0xa2, 0x8e, 0xc6, 0x1b, 0xac, 0x29, 0x74, 0x19, 0xfa, 0xc2, 0xaa, 0xdf, 0x54, 0x7e, + 0x80, 0x2c, 0x5d, 0x78, 0x85, 0x95, 0x1c, 0xec, 0x95, 0x90, 0xd9, 0x1c, 0x2d, 0xc6, 0x02, 0x1f, + 0xbd, 0x0d, 0x23, 0xec, 0x97, 0xb2, 0xa6, 0x2c, 0xe6, 0x4b, 0x60, 0x2a, 0x3a, 0x22, 0x37, 0x35, + 0x36, 0x8a, 0xb0, 0x49, 0x6a, 0x7a, 0x13, 0x06, 0xd5, 0x67, 0x3d, 0x54, 0x6d, 0xfd, 0xff, 0x59, + 0x84, 0xc9, 0x8c, 0x35, 0x87, 0x42, 0x63, 0x26, 0x2e, 0x75, 0xb9, 0x54, 0xdf, 0xe3, 0x5c, 0x84, + 0xec, 0x01, 0x58, 0x13, 0x6b, 0xab, 0xeb, 0x46, 0xd7, 0x43, 0x92, 0x6c, 0x94, 0x16, 0x75, 0x6e, + 0x94, 0x36, 0x76, 0x64, 0x43, 0x4d, 0x1b, 0x52, 0x3d, 0x7d, 0xa8, 0x73, 0xfa, 0x87, 0x3d, 0x70, + 0x2c, 0xcb, 0xc4, 0x04, 0x7d, 0x0e, 0xfa, 0x98, 0xa3, 0x9a, 0x14, 0x9c, 0xbd, 0xd8, 0xad, 0x71, + 0xca, 0x0c, 0xf3, 0x75, 0x13, 0xa1, 0x69, 0x67, 0xe4, 0x71, 0xc4, 0x0b, 0x3b, 0x0e, 0xb3, 0x68, + 0x93, 0x85, 0x8c, 0x12, 0xb7, 0xa7, 0x3c, 0x3e, 0x3e, 0xd2, 0x75, 0x07, 0xc4, 0xfd, 0x1b, 0x26, + 0x2c, 0xb5, 0x64, 0x71, 0x67, 0x4b, 0x2d, 0xd9, 0x32, 0x5a, 0x86, 0xbe, 0x2a, 0x37, 0x01, 0x2a, + 0x76, 0x3e, 0xc2, 0xb8, 0xfd, 0x8f, 0x3a, 0x80, 0x85, 0xdd, 0x8f, 0x20, 0x30, 0xed, 0xc2, 0x90, + 0x36, 0x30, 0x0f, 0x75, 0xf1, 0x6c, 0xd3, 0x8b, 0x4f, 0x1b, 0x82, 0x87, 0xba, 0x80, 0x7e, 0x46, + 0xbb, 0xfb, 0xc5, 0x79, 0xf0, 0x61, 0x83, 0x77, 0x3a, 0x9d, 0x70, 0x1f, 0x4c, 0xec, 0x2b, 0xc6, + 0x4b, 0x55, 0xcc, 0x98, 0xee, 0xb9, 0xa9, 0xa1, 0xcc, 0x0b, 0xbf, 0x7d, 0x1c, 0x77, 0xfb, 0x67, + 0x2d, 0x48, 0x38, 0x78, 0x29, 0x71, 0xa7, 0x95, 0x2b, 0xee, 0x3c, 0x07, 0x3d, 0x81, 0x5f, 0x27, + 0xc9, 0xd4, 0xfb, 0xd8, 0xaf, 0x13, 0xcc, 0x20, 0x14, 0x23, 0x8a, 0x85, 0x58, 0xc3, 0xfa, 0x03, + 0x5d, 0x3c, 0xbd, 0x1f, 0x83, 0xde, 0x3a, 0xd9, 0x21, 0xf5, 0x64, 0x86, 0xd4, 0xeb, 0xb4, 0x10, + 0x73, 0x98, 0xfd, 0xdb, 0x3d, 0x70, 0xa6, 0x6d, 0x64, 0x39, 0xca, 0x60, 0x6e, 0x3a, 0x11, 0xb9, + 0xe3, 0xec, 0x26, 0x33, 0x03, 0x5e, 0xe1, 0xc5, 0x58, 0xc2, 0x99, 0xb3, 0x35, 0xcf, 0x94, 0x93, + 0x10, 0x0e, 0x8b, 0x04, 0x39, 0x02, 0x6a, 0x0a, 0x1b, 0x8b, 0x0f, 0x42, 0xd8, 0xf8, 0x3c, 0x40, + 0x18, 0xd6, 0xb9, 0x1d, 0x67, 0x4d, 0x78, 0x71, 0xc7, 0x19, 0x95, 0x2a, 0xd7, 0x05, 0x04, 0x6b, + 0x58, 0x68, 0x01, 0xc6, 0x9b, 0x81, 0x1f, 0x71, 0x59, 0xfb, 0x02, 0x37, 0x75, 0xee, 0x35, 0x83, + 0x7a, 0x95, 0x13, 0x70, 0x9c, 0xaa, 0x81, 0x5e, 0x82, 0x21, 0x11, 0xe8, 0xab, 0xec, 0xfb, 0x75, + 0x21, 0xde, 0x53, 0xd6, 0xbf, 0x95, 0x18, 0x84, 0x75, 0x3c, 0xad, 0x1a, 0x13, 0xe0, 0xf7, 0x67, + 0x56, 0xe3, 0x42, 0x7c, 0x0d, 0x2f, 0x91, 0x14, 0x60, 0xa0, 0xab, 0xa4, 0x00, 0xb1, 0xc0, 0x73, + 0xb0, 0x6b, 0x7d, 0x32, 0x74, 0x14, 0x11, 0x7e, 0xad, 0x07, 0x26, 0xc5, 0xc2, 0x79, 0xd8, 0xcb, + 0x65, 0x3d, 0xbd, 0x5c, 0x1e, 0x84, 0x48, 0xf4, 0x83, 0x35, 0x73, 0xd4, 0x6b, 0xe6, 0x27, 0x2d, + 0x30, 0x79, 0x48, 0xf4, 0x9f, 0xe5, 0xa6, 0x56, 0x7d, 0x29, 0x97, 0x27, 0x8d, 0x23, 0x86, 0xbf, + 0xb7, 0x24, 0xab, 0xf6, 0xff, 0x65, 0xc1, 0xa3, 0x1d, 0x29, 0xa2, 0x45, 0x18, 0x64, 0x8c, 0xae, + 0xf6, 0x2e, 0x7e, 0x52, 0xb9, 0x42, 0x48, 0x40, 0x0e, 0xdf, 0x1d, 0xd7, 0x44, 0x8b, 0xa9, 0x1c, + 0xb6, 0x4f, 0x65, 0xe4, 0xb0, 0x3d, 0x6e, 0x0c, 0xcf, 0x7d, 0x26, 0xb1, 0xfd, 0x12, 0xbd, 0x71, + 0x4c, 0x7f, 0xca, 0x8f, 0x18, 0xe2, 0x5c, 0x3b, 0x21, 0xce, 0x45, 0x26, 0xb6, 0x76, 0x87, 0xbc, + 0x01, 0xe3, 0x2c, 0x02, 0x28, 0x73, 0xcc, 0x11, 0x8e, 0x98, 0x85, 0xd8, 0xf8, 0xfe, 0x7a, 0x02, + 0x86, 0x53, 0xd8, 0xf6, 0xdf, 0x15, 0xa1, 0x8f, 0x6f, 0xbf, 0x23, 0x78, 0xf8, 0x3e, 0x03, 0x83, + 0x6e, 0xa3, 0xd1, 0xe2, 0x69, 0x49, 0x7b, 0x63, 0x53, 0xee, 0x65, 0x59, 0x88, 0x63, 0x38, 0x5a, + 0x12, 0x9a, 0x84, 0x36, 0x41, 0xc6, 0x79, 0xc7, 0x67, 0x16, 0x9c, 0xc8, 0xe1, 0x5c, 0x9c, 0xba, + 0x67, 0x63, 0x9d, 0x03, 0xfa, 0x34, 0x40, 0x18, 0x05, 0xae, 0xb7, 0x49, 0xcb, 0x44, 0x26, 0x8a, + 0xa7, 0xdb, 0x50, 0xab, 0x28, 0x64, 0x4e, 0x33, 0x3e, 0x73, 0x14, 0x00, 0x6b, 0x14, 0xd1, 0x8c, + 0x71, 0xd3, 0x4f, 0x27, 0xe6, 0x0e, 0x38, 0xd5, 0x78, 0xce, 0xa6, 0x5f, 0x86, 0x41, 0x45, 0xbc, + 0x93, 0x5c, 0x71, 0x58, 0x67, 0xd8, 0x3e, 0x06, 0x63, 0x89, 0xbe, 0x1d, 0x4a, 0x2c, 0xf9, 0x3b, + 0x16, 0x8c, 0xf1, 0xce, 0x2c, 0x7a, 0x3b, 0xe2, 0x36, 0xb8, 0x07, 0xc7, 0xea, 0x19, 0xa7, 0xb2, + 0x98, 0xfe, 0xee, 0x4f, 0x71, 0x25, 0x86, 0xcc, 0x82, 0xe2, 0xcc, 0x36, 0xd0, 0x05, 0xba, 0xe3, + 0xe8, 0xa9, 0xeb, 0xd4, 0x45, 0x34, 0x91, 0x61, 0xbe, 0xdb, 0x78, 0x19, 0x56, 0x50, 0xfb, 0xaf, + 0x2c, 0x98, 0xe0, 0x3d, 0xbf, 0x46, 0x76, 0xd5, 0xd9, 0xf4, 0x9d, 0xec, 0xbb, 0x48, 0x88, 0x5d, + 0xc8, 0x49, 0x88, 0xad, 0x7f, 0x5a, 0xb1, 0xed, 0xa7, 0x7d, 0xd5, 0x02, 0xb1, 0x42, 0x8e, 0x40, + 0xd2, 0xf2, 0x7d, 0xa6, 0xa4, 0x65, 0x3a, 0x7f, 0x13, 0xe4, 0x88, 0x58, 0xfe, 0xc5, 0x82, 0x71, + 0x8e, 0x10, 0x5b, 0x41, 0x7c, 0x47, 0xe7, 0x61, 0xce, 0xfc, 0xa2, 0x4c, 0xb3, 0xd6, 0x6b, 0x64, + 0x77, 0xcd, 0x2f, 0x3b, 0xd1, 0x56, 0xf6, 0x47, 0x19, 0x93, 0xd5, 0xd3, 0x76, 0xb2, 0x6a, 0x72, + 0x03, 0x19, 0x89, 0x17, 0x3b, 0x08, 0x80, 0x0f, 0x9b, 0x78, 0xd1, 0xfe, 0x7b, 0x0b, 0x10, 0x6f, + 0xc6, 0x60, 0xdc, 0x28, 0x3b, 0xc4, 0x4a, 0xb5, 0x8b, 0x2e, 0x3e, 0x9a, 0x14, 0x04, 0x6b, 0x58, + 0x0f, 0x64, 0x78, 0x12, 0xa6, 0x2c, 0xc5, 0xce, 0xa6, 0x2c, 0x87, 0x18, 0xd1, 0xaf, 0xf6, 0x43, + 0xd2, 0x15, 0x13, 0xdd, 0x84, 0xe1, 0xaa, 0xd3, 0x74, 0x6e, 0xbb, 0x75, 0x37, 0x72, 0x49, 0xd8, + 0xce, 0xce, 0x6d, 0x5e, 0xc3, 0x13, 0xc6, 0x07, 0x5a, 0x09, 0x36, 0xe8, 0xa0, 0x19, 0x80, 0x66, + 0xe0, 0xee, 0xb8, 0x75, 0xb2, 0xc9, 0x04, 0x42, 0x2c, 0x7e, 0x11, 0x37, 0xba, 0x93, 0xa5, 0x58, + 0xc3, 0xc8, 0x08, 0x1b, 0x52, 0x7c, 0xc8, 0x61, 0x43, 0xe0, 0xc8, 0xc2, 0x86, 0xf4, 0x1c, 0x2a, + 0x6c, 0xc8, 0xc0, 0xa1, 0xc3, 0x86, 0xf4, 0x76, 0x15, 0x36, 0x04, 0xc3, 0x09, 0xc9, 0x7b, 0xd2, + 0xff, 0x4b, 0x6e, 0x9d, 0x88, 0x07, 0x07, 0x0f, 0xba, 0x34, 0xbd, 0xbf, 0x57, 0x3a, 0x81, 0x33, + 0x31, 0x70, 0x4e, 0x4d, 0xf4, 0x71, 0x98, 0x72, 0xea, 0x75, 0xff, 0x8e, 0x9a, 0xd4, 0xc5, 0xb0, + 0xea, 0xd4, 0xb9, 0x72, 0xa9, 0x9f, 0x51, 0x3d, 0xbd, 0xbf, 0x57, 0x9a, 0x9a, 0xcd, 0xc1, 0xc1, + 0xb9, 0xb5, 0xd1, 0x6b, 0x30, 0xd8, 0x0c, 0xfc, 0xea, 0x8a, 0xe6, 0x2f, 0x7e, 0x96, 0x0e, 0x60, + 0x59, 0x16, 0x1e, 0xec, 0x95, 0x46, 0xd4, 0x1f, 0x76, 0xe1, 0xc7, 0x15, 0x32, 0x22, 0x72, 0x0c, + 0x3d, 0xec, 0x88, 0x1c, 0xc3, 0x0f, 0x38, 0x22, 0x87, 0xbd, 0x0d, 0x93, 0x15, 0x12, 0xb8, 0x4e, + 0xdd, 0xbd, 0x47, 0x79, 0x72, 0x79, 0x06, 0xae, 0xc1, 0x60, 0x90, 0x38, 0xf5, 0xbb, 0x0a, 0x2e, + 0xae, 0xc9, 0x65, 0xe4, 0x29, 0x1f, 0x13, 0xb2, 0xff, 0xbd, 0x05, 0xfd, 0xc2, 0xbd, 0xf3, 0x08, + 0x38, 0xd3, 0x59, 0x43, 0x25, 0x53, 0xca, 0x9e, 0x14, 0xd6, 0x99, 0x5c, 0x65, 0xcc, 0x72, 0x42, + 0x19, 0xf3, 0x68, 0x3b, 0x22, 0xed, 0xd5, 0x30, 0xff, 0x75, 0x91, 0xbe, 0x10, 0x8c, 0x40, 0x03, + 0x0f, 0x7f, 0x08, 0x56, 0xa1, 0x3f, 0x14, 0x8e, 0xee, 0x85, 0x7c, 0x5f, 0x9e, 0xe4, 0x24, 0xc6, + 0x36, 0x90, 0xc2, 0xb5, 0x5d, 0x12, 0xc9, 0xf4, 0xa0, 0x2f, 0x3e, 0x44, 0x0f, 0xfa, 0x4e, 0xa1, + 0x18, 0x7a, 0x1e, 0x44, 0x28, 0x06, 0xfb, 0x1b, 0xec, 0x76, 0xd6, 0xcb, 0x8f, 0x80, 0x71, 0xbb, + 0x62, 0xde, 0xe3, 0x76, 0x9b, 0x95, 0x25, 0x3a, 0x95, 0xc3, 0xc0, 0xfd, 0x96, 0x05, 0x67, 0x32, + 0xbe, 0x4a, 0xe3, 0xe6, 0x9e, 0x85, 0x01, 0xa7, 0x55, 0x73, 0xd5, 0x5e, 0xd6, 0xb4, 0xc5, 0xb3, + 0xa2, 0x1c, 0x2b, 0x0c, 0x34, 0x0f, 0x13, 0xe4, 0x6e, 0xd3, 0xe5, 0x6a, 0x78, 0xdd, 0x74, 0xbc, + 0xc8, 0x7d, 0x82, 0x17, 0x93, 0x40, 0x9c, 0xc6, 0x57, 0xe1, 0xdc, 0x8a, 0xb9, 0xe1, 0xdc, 0x7e, + 0xdd, 0x82, 0x21, 0xe5, 0xea, 0xfd, 0xd0, 0x47, 0xfb, 0x0d, 0x73, 0xb4, 0x1f, 0x69, 0x33, 0xda, + 0x39, 0xc3, 0xfc, 0x97, 0x05, 0xd5, 0xdf, 0xb2, 0x1f, 0x44, 0x5d, 0x70, 0x89, 0xf7, 0xef, 0xf6, + 0x72, 0x09, 0x86, 0x9c, 0x66, 0x53, 0x02, 0xa4, 0xfd, 0x22, 0x4b, 0x15, 0x11, 0x17, 0x63, 0x1d, + 0x47, 0x79, 0xe1, 0x14, 0x73, 0xbd, 0x70, 0x6a, 0x00, 0x91, 0x13, 0x6c, 0x92, 0x88, 0x96, 0x09, + 0x73, 0xeb, 0xfc, 0xf3, 0xa6, 0x15, 0xb9, 0xf5, 0x19, 0xd7, 0x8b, 0xc2, 0x28, 0x98, 0x59, 0xf6, + 0xa2, 0x1b, 0x01, 0x7f, 0xa6, 0x6a, 0x41, 0x13, 0x15, 0x2d, 0xac, 0xd1, 0x95, 0x61, 0x4d, 0x58, + 0x1b, 0xbd, 0xa6, 0x21, 0xcc, 0xaa, 0x28, 0xc7, 0x0a, 0xc3, 0x7e, 0x99, 0xdd, 0x3e, 0x6c, 0x4c, + 0x0f, 0x17, 0x0c, 0xf0, 0x1f, 0x86, 0xd5, 0x6c, 0x30, 0x95, 0xf0, 0x82, 0x1e, 0x72, 0xb0, 0xfd, + 0x61, 0x4f, 0x1b, 0xd6, 0xfd, 0x59, 0xe3, 0xb8, 0x84, 0xe8, 0x93, 0x29, 0xe3, 0xa6, 0xe7, 0x3a, + 0xdc, 0x1a, 0x87, 0x30, 0x67, 0x62, 0x79, 0xe3, 0x58, 0x56, 0xad, 0xe5, 0xb2, 0xd8, 0x17, 0x5a, + 0xde, 0x38, 0x01, 0xc0, 0x31, 0x0e, 0x65, 0xd8, 0xd4, 0x9f, 0x70, 0x0a, 0xc5, 0xe1, 0xc5, 0x15, + 0x76, 0x88, 0x35, 0x0c, 0x74, 0x51, 0x08, 0x2d, 0xb8, 0xee, 0xe1, 0x91, 0x84, 0xd0, 0x42, 0x0e, + 0x97, 0x26, 0x69, 0xba, 0x04, 0x43, 0xe4, 0x6e, 0x44, 0x02, 0xcf, 0xa9, 0xd3, 0x16, 0x7a, 0xe3, + 0x88, 0xb8, 0x8b, 0x71, 0x31, 0xd6, 0x71, 0xd0, 0x1a, 0x8c, 0x85, 0x5c, 0x96, 0xa7, 0x92, 0x5a, + 0x70, 0x99, 0xe8, 0xd3, 0xca, 0xc9, 0xde, 0x04, 0x1f, 0xb0, 0x22, 0x7e, 0x3a, 0xc9, 0xd0, 0x23, + 0x49, 0x12, 0xe8, 0x75, 0x18, 0xad, 0xfb, 0x4e, 0x6d, 0xce, 0xa9, 0x3b, 0x5e, 0x95, 0x8d, 0xcf, + 0x80, 0x11, 0x7f, 0x72, 0xf4, 0xba, 0x01, 0xc5, 0x09, 0x6c, 0xca, 0x20, 0xea, 0x25, 0x22, 0x11, + 0x8b, 0xe3, 0x6d, 0x92, 0x70, 0x6a, 0x90, 0x7d, 0x15, 0x63, 0x10, 0xaf, 0xe7, 0xe0, 0xe0, 0xdc, + 0xda, 0xe8, 0x32, 0x0c, 0xcb, 0xcf, 0xd7, 0x22, 0xf5, 0xc4, 0x0e, 0x4d, 0x1a, 0x0c, 0x1b, 0x98, + 0x28, 0x84, 0xe3, 0xf2, 0xff, 0x5a, 0xe0, 0x6c, 0x6c, 0xb8, 0x55, 0x11, 0xbe, 0x82, 0x3b, 0x7f, + 0x7f, 0x4c, 0x7a, 0x9a, 0x2e, 0x66, 0x21, 0x1d, 0xec, 0x95, 0x4e, 0x8b, 0x51, 0xcb, 0x84, 0xe3, + 0x6c, 0xda, 0x68, 0x05, 0x26, 0xb9, 0x0d, 0xcc, 0xfc, 0x16, 0xa9, 0x6e, 0xcb, 0x0d, 0xc7, 0xb8, + 0x46, 0xcd, 0xf1, 0xe7, 0x6a, 0x1a, 0x05, 0x67, 0xd5, 0x43, 0xef, 0xc0, 0x54, 0xb3, 0x75, 0xbb, + 0xee, 0x86, 0x5b, 0xab, 0x7e, 0xc4, 0x4c, 0xc8, 0x66, 0x6b, 0xb5, 0x80, 0x84, 0xdc, 0x37, 0x98, + 0x5d, 0xbd, 0x32, 0xba, 0x52, 0x39, 0x07, 0x0f, 0xe7, 0x52, 0x40, 0xf7, 0xe0, 0x78, 0x62, 0x21, + 0x88, 0x30, 0x29, 0xa3, 0xf9, 0x29, 0xad, 0x2a, 0x59, 0x15, 0x44, 0xc4, 0xa1, 0x2c, 0x10, 0xce, + 0x6e, 0x02, 0xbd, 0x02, 0xe0, 0x36, 0x97, 0x9c, 0x86, 0x5b, 0xa7, 0xcf, 0xd1, 0x49, 0xb6, 0x46, + 0xe8, 0xd3, 0x04, 0x96, 0xcb, 0xb2, 0x94, 0x9e, 0xcd, 0xe2, 0xdf, 0x2e, 0xd6, 0xb0, 0xd1, 0x75, + 0x18, 0x15, 0xff, 0x76, 0xc5, 0x94, 0x4e, 0xa8, 0xec, 0xa7, 0xa3, 0xb2, 0x86, 0x9a, 0xc7, 0x44, + 0x09, 0x4e, 0xd4, 0x45, 0x9b, 0x70, 0x46, 0xa6, 0x5e, 0xd5, 0xd7, 0xa7, 0x9c, 0x83, 0x90, 0xe5, + 0x91, 0x1a, 0xe0, 0x3e, 0x45, 0xb3, 0xed, 0x10, 0x71, 0x7b, 0x3a, 0xf4, 0x5e, 0xd7, 0x97, 0x39, + 0xf7, 0x18, 0x3f, 0x1e, 0x47, 0xf1, 0xbc, 0x9e, 0x04, 0xe2, 0x34, 0x3e, 0xf2, 0xe1, 0xb8, 0xeb, + 0x65, 0xad, 0xea, 0x13, 0x8c, 0xd0, 0x47, 0xb9, 0xb3, 0x7c, 0xfb, 0x15, 0x9d, 0x09, 0xc7, 0xd9, + 0x74, 0xd1, 0x32, 0x4c, 0x46, 0xbc, 0x60, 0xc1, 0x0d, 0x79, 0x9a, 0x1a, 0xfa, 0xec, 0x3b, 0xc9, + 0x9a, 0x3b, 0x49, 0x57, 0xf3, 0x5a, 0x1a, 0x8c, 0xb3, 0xea, 0xbc, 0x37, 0x03, 0xd0, 0x6f, 0x5a, + 0xb4, 0xb6, 0xc6, 0xe8, 0xa3, 0xcf, 0xc0, 0xb0, 0x3e, 0x3e, 0x82, 0x69, 0x39, 0x9f, 0xcd, 0x07, + 0x6b, 0xc7, 0x0b, 0x7f, 0x26, 0xa8, 0x23, 0x44, 0x87, 0x61, 0x83, 0x22, 0xaa, 0x66, 0x04, 0xb9, + 0xb8, 0xd8, 0x1d, 0x53, 0xd4, 0xbd, 0xfd, 0x23, 0x81, 0xec, 0x9d, 0x83, 0xae, 0xc3, 0x40, 0xb5, + 0xee, 0x12, 0x2f, 0x5a, 0x2e, 0xb7, 0x0b, 0xae, 0x3a, 0x2f, 0x70, 0xc4, 0x56, 0x14, 0xd9, 0xa5, + 0x78, 0x19, 0x56, 0x14, 0xec, 0xcb, 0x30, 0x54, 0xa9, 0x13, 0xd2, 0xe4, 0x7e, 0x5c, 0xe8, 0x29, + 0xf6, 0x30, 0x61, 0xac, 0xa5, 0xc5, 0x58, 0x4b, 0xfd, 0xcd, 0xc1, 0x98, 0x4a, 0x09, 0xb7, 0xff, + 0xb8, 0x00, 0xa5, 0x0e, 0x49, 0xce, 0x12, 0xfa, 0x36, 0xab, 0x2b, 0x7d, 0xdb, 0x2c, 0x8c, 0xc5, + 0xff, 0x74, 0x51, 0x9e, 0x32, 0x86, 0xbe, 0x69, 0x82, 0x71, 0x12, 0xbf, 0x6b, 0xbf, 0x16, 0x5d, + 0x65, 0xd7, 0xd3, 0xd1, 0x33, 0xcb, 0x50, 0xd5, 0xf7, 0x76, 0xff, 0xf6, 0xce, 0x55, 0xbb, 0xda, + 0xdf, 0x28, 0xc0, 0x71, 0x35, 0x84, 0xdf, 0xbb, 0x03, 0xb7, 0x9e, 0x1e, 0xb8, 0x07, 0xa0, 0xb4, + 0xb6, 0x6f, 0x40, 0x1f, 0x8f, 0xf8, 0xda, 0x05, 0xcf, 0xff, 0x98, 0x19, 0x7c, 0x5f, 0xb1, 0x99, + 0x46, 0x00, 0xfe, 0x1f, 0xb3, 0x60, 0x2c, 0xe1, 0x20, 0x89, 0xb0, 0xe6, 0x45, 0x7f, 0x3f, 0x7c, + 0x79, 0x16, 0xc7, 0x7f, 0x0e, 0x7a, 0xb6, 0x7c, 0x65, 0xa4, 0xac, 0x30, 0xae, 0xfa, 0x61, 0x84, + 0x19, 0xc4, 0xfe, 0x6b, 0x0b, 0x7a, 0xd7, 0x1c, 0xd7, 0x8b, 0xa4, 0xf6, 0xc3, 0xca, 0xd1, 0x7e, + 0x74, 0xf3, 0x5d, 0xe8, 0x25, 0xe8, 0x23, 0x1b, 0x1b, 0xa4, 0x1a, 0x89, 0x59, 0x95, 0xd1, 0x34, + 0xfa, 0x16, 0x59, 0x29, 0x65, 0x42, 0x59, 0x63, 0xfc, 0x2f, 0x16, 0xc8, 0xe8, 0x16, 0x0c, 0x46, + 0x6e, 0x83, 0xcc, 0xd6, 0x6a, 0xc2, 0x26, 0xe0, 0x3e, 0x42, 0xc0, 0xac, 0x49, 0x02, 0x38, 0xa6, + 0x65, 0x7f, 0xb9, 0x00, 0x10, 0x47, 0x98, 0xeb, 0xf4, 0x89, 0x73, 0x29, 0x6d, 0xf1, 0xf9, 0x0c, + 0x6d, 0x31, 0x8a, 0x09, 0x66, 0xa8, 0x8a, 0xd5, 0x30, 0x15, 0xbb, 0x1a, 0xa6, 0x9e, 0xc3, 0x0c, + 0xd3, 0x3c, 0x4c, 0xc4, 0x11, 0xf2, 0xcc, 0x00, 0xa1, 0xec, 0xfe, 0x5e, 0x4b, 0x02, 0x71, 0x1a, + 0xdf, 0x26, 0x70, 0x4e, 0x05, 0x0a, 0x13, 0x77, 0x21, 0x73, 0x25, 0xd0, 0xb5, 0xef, 0x1d, 0xc6, + 0x29, 0x56, 0x87, 0x17, 0x72, 0xd5, 0xe1, 0xbf, 0x60, 0xc1, 0xb1, 0x64, 0x3b, 0xcc, 0xef, 0xfe, + 0x8b, 0x16, 0x1c, 0x8f, 0x73, 0xfc, 0xa4, 0x4d, 0x10, 0x5e, 0x6c, 0x1b, 0xfc, 0x2c, 0xa7, 0xc7, + 0x71, 0xd8, 0x96, 0x95, 0x2c, 0xd2, 0x38, 0xbb, 0x45, 0xfb, 0xdf, 0xf5, 0xc0, 0x54, 0x5e, 0xd4, + 0x34, 0xe6, 0x69, 0xe4, 0xdc, 0xad, 0x6c, 0x93, 0x3b, 0xc2, 0x9f, 0x23, 0xf6, 0x34, 0xe2, 0xc5, + 0x58, 0xc2, 0x93, 0x69, 0x9d, 0x0a, 0x5d, 0xa6, 0x75, 0xda, 0x82, 0x89, 0x3b, 0x5b, 0xc4, 0x5b, + 0xf7, 0x42, 0x27, 0x72, 0xc3, 0x0d, 0x97, 0x29, 0xd0, 0xf9, 0xba, 0x79, 0x45, 0x7a, 0x5d, 0xdc, + 0x4a, 0x22, 0x1c, 0xec, 0x95, 0xce, 0x18, 0x05, 0x71, 0x97, 0xf9, 0x41, 0x82, 0xd3, 0x44, 0xd3, + 0x59, 0xb1, 0x7a, 0x1e, 0x72, 0x56, 0xac, 0x86, 0x2b, 0xcc, 0x6e, 0xa4, 0x1b, 0x09, 0x7b, 0xb6, + 0xae, 0xa8, 0x52, 0xac, 0x61, 0xa0, 0x4f, 0x01, 0xd2, 0xd3, 0x1a, 0x1a, 0x41, 0x6b, 0x9f, 0xdb, + 0xdf, 0x2b, 0xa1, 0xd5, 0x14, 0xf4, 0x60, 0xaf, 0x34, 0x49, 0x4b, 0x97, 0x3d, 0xfa, 0xfc, 0x8d, + 0x23, 0xfd, 0x65, 0x10, 0x42, 0xb7, 0x60, 0x9c, 0x96, 0xb2, 0x1d, 0x25, 0x23, 0xe2, 0xf2, 0x27, + 0xeb, 0x33, 0xfb, 0x7b, 0xa5, 0xf1, 0xd5, 0x04, 0x2c, 0x8f, 0x74, 0x8a, 0x48, 0x46, 0x72, 0xac, + 0x81, 0x6e, 0x93, 0x63, 0xd9, 0x5f, 0xb4, 0xe0, 0x14, 0xbd, 0xe0, 0x6a, 0xd7, 0x73, 0xb4, 0xe8, + 0x4e, 0xd3, 0xe5, 0x7a, 0x1a, 0x71, 0xd5, 0x30, 0x59, 0x5d, 0x79, 0x99, 0x6b, 0x69, 0x14, 0x94, + 0x9e, 0xf0, 0xdb, 0xae, 0x57, 0x4b, 0x9e, 0xf0, 0xd7, 0x5c, 0xaf, 0x86, 0x19, 0x44, 0x5d, 0x59, + 0xc5, 0xdc, 0x08, 0xfb, 0x5f, 0xa3, 0x7b, 0x95, 0xf6, 0xe5, 0x3b, 0xda, 0x0d, 0xf4, 0x8c, 0xae, + 0x53, 0x15, 0xe6, 0x93, 0xb9, 0xfa, 0xd4, 0x2f, 0x58, 0x20, 0xbc, 0xdf, 0xbb, 0xb8, 0x93, 0xdf, + 0x86, 0xe1, 0x9d, 0x74, 0xca, 0xd7, 0x73, 0xf9, 0xe1, 0x00, 0x44, 0xa2, 0x57, 0xc5, 0xa2, 0x1b, + 0xe9, 0x5d, 0x0d, 0x5a, 0x76, 0x0d, 0x04, 0x74, 0x81, 0x30, 0xad, 0x46, 0xe7, 0xde, 0x3c, 0x0f, + 0x50, 0x63, 0xb8, 0x2c, 0x0f, 0x7c, 0xc1, 0xe4, 0xb8, 0x16, 0x14, 0x04, 0x6b, 0x58, 0xf6, 0xaf, + 0x16, 0x61, 0x48, 0xa6, 0x18, 0x6d, 0x79, 0xdd, 0xc8, 0x1e, 0x75, 0xc6, 0xa9, 0xd0, 0x91, 0x71, + 0x7a, 0x07, 0x26, 0x02, 0x52, 0x6d, 0x05, 0xa1, 0xbb, 0x43, 0x24, 0x58, 0x6c, 0x92, 0x19, 0x9e, + 0xe0, 0x21, 0x01, 0x3c, 0x60, 0x21, 0xb2, 0x12, 0x85, 0x4c, 0x69, 0x9c, 0x26, 0x84, 0x2e, 0xc2, + 0x20, 0x13, 0xbd, 0x97, 0x63, 0x81, 0xb0, 0x12, 0x7c, 0xad, 0x48, 0x00, 0x8e, 0x71, 0xd8, 0xe3, + 0xa0, 0x75, 0x9b, 0xa1, 0x27, 0x3c, 0xc1, 0x2b, 0xbc, 0x18, 0x4b, 0x38, 0xfa, 0x38, 0x8c, 0xf3, + 0x7a, 0x81, 0xdf, 0x74, 0x36, 0xb9, 0x4a, 0xb0, 0x57, 0x85, 0xd7, 0x19, 0x5f, 0x49, 0xc0, 0x0e, + 0xf6, 0x4a, 0xc7, 0x92, 0x65, 0xac, 0xdb, 0x29, 0x2a, 0xcc, 0xf2, 0x8f, 0x37, 0x42, 0xef, 0x8c, + 0x94, 0xc1, 0x60, 0x0c, 0xc2, 0x3a, 0x9e, 0xfd, 0xcf, 0x16, 0x4c, 0x68, 0x53, 0xd5, 0x75, 0x8e, + 0x0d, 0x63, 0x90, 0x0a, 0x5d, 0x0c, 0xd2, 0xe1, 0xa2, 0x3d, 0x64, 0xce, 0x70, 0xcf, 0x03, 0x9a, + 0x61, 0xfb, 0x33, 0x80, 0xd2, 0xf9, 0x6b, 0xd1, 0x9b, 0xdc, 0x90, 0xdf, 0x0d, 0x48, 0xad, 0x9d, + 0xc2, 0x5f, 0x8f, 0x9c, 0x23, 0x3d, 0x57, 0x79, 0x2d, 0xac, 0xea, 0xdb, 0x3f, 0xde, 0x03, 0xe3, + 0xc9, 0x58, 0x1d, 0xe8, 0x2a, 0xf4, 0x71, 0x2e, 0x5d, 0x90, 0x6f, 0x63, 0x4f, 0xa6, 0x45, 0xf8, + 0xe0, 0xf9, 0x6f, 0x38, 0x77, 0x2f, 0xea, 0xa3, 0x77, 0x60, 0xa8, 0xe6, 0xdf, 0xf1, 0xee, 0x38, + 0x41, 0x6d, 0xb6, 0xbc, 0x2c, 0x4e, 0x88, 0x4c, 0x01, 0xd4, 0x42, 0x8c, 0xa6, 0x47, 0x0d, 0x61, + 0xb6, 0x13, 0x31, 0x08, 0xeb, 0xe4, 0xd0, 0x1a, 0x4b, 0xc9, 0xb4, 0xe1, 0x6e, 0xae, 0x38, 0xcd, + 0x76, 0x5e, 0x5d, 0xf3, 0x12, 0x49, 0xa3, 0x3c, 0x22, 0xf2, 0x36, 0x71, 0x00, 0x8e, 0x09, 0xa1, + 0xcf, 0xc1, 0x64, 0x98, 0xa3, 0x12, 0xcb, 0x4b, 0x67, 0xde, 0x4e, 0x4b, 0xc4, 0x85, 0x29, 0x59, + 0xca, 0xb3, 0xac, 0x66, 0xd0, 0x5d, 0x40, 0x42, 0xf4, 0xbc, 0x16, 0xb4, 0xc2, 0x68, 0xae, 0xe5, + 0xd5, 0xea, 0x32, 0x65, 0xd3, 0x87, 0xb3, 0xe5, 0x04, 0x49, 0x6c, 0xad, 0x6d, 0x16, 0x12, 0x38, + 0x8d, 0x81, 0x33, 0xda, 0xb0, 0xbf, 0xd0, 0x03, 0xd3, 0x32, 0x61, 0x74, 0x86, 0xf7, 0xca, 0xe7, + 0xad, 0x84, 0xfb, 0xca, 0x2b, 0xf9, 0x07, 0xfd, 0x43, 0x73, 0x62, 0xf9, 0x52, 0xda, 0x89, 0xe5, + 0xb5, 0x43, 0x76, 0xe3, 0x81, 0xb9, 0xb2, 0x7c, 0xcf, 0xfa, 0x9f, 0xec, 0x1f, 0x03, 0xe3, 0x6a, + 0x46, 0x98, 0xc7, 0x5b, 0x2f, 0x4b, 0xd5, 0x51, 0xce, 0xf3, 0xff, 0xaa, 0xc0, 0x31, 0x2e, 0xfb, + 0x61, 0x19, 0x95, 0x9d, 0x9d, 0xb3, 0x8a, 0x0e, 0xa5, 0x49, 0x1a, 0xcd, 0x68, 0x77, 0xc1, 0x0d, + 0x44, 0x8f, 0x33, 0x69, 0x2e, 0x0a, 0x9c, 0x34, 0x4d, 0x09, 0xc1, 0x8a, 0x0e, 0xda, 0x81, 0x89, + 0x4d, 0x16, 0xf1, 0x49, 0xcb, 0xdd, 0x2c, 0xce, 0x85, 0xcc, 0x7d, 0x7b, 0x65, 0x7e, 0x31, 0x3f, + 0xd1, 0x33, 0x7f, 0xfc, 0xa5, 0x50, 0x70, 0xba, 0x09, 0xba, 0x35, 0x8e, 0x39, 0x77, 0xc2, 0xc5, + 0xba, 0x13, 0x46, 0x6e, 0x75, 0xae, 0xee, 0x57, 0xb7, 0x2b, 0x91, 0x1f, 0xc8, 0x04, 0x8f, 0x99, + 0x6f, 0xaf, 0xd9, 0x5b, 0x95, 0x14, 0xbe, 0xd1, 0xfc, 0xd4, 0xfe, 0x5e, 0xe9, 0x58, 0x16, 0x16, + 0xce, 0x6c, 0x0b, 0xad, 0x42, 0xff, 0xa6, 0x1b, 0x61, 0xd2, 0xf4, 0xc5, 0x69, 0x91, 0x79, 0x14, + 0x5e, 0xe1, 0x28, 0x46, 0x4b, 0x2c, 0x22, 0x95, 0x00, 0x60, 0x49, 0x04, 0xbd, 0xa9, 0x2e, 0x81, + 0xbe, 0x7c, 0x01, 0x6c, 0xda, 0xf6, 0x2e, 0xf3, 0x1a, 0x78, 0x1d, 0x8a, 0xde, 0x46, 0xd8, 0x2e, + 0x16, 0xcf, 0xea, 0x92, 0x21, 0x3f, 0x9b, 0xeb, 0xa7, 0x4f, 0xe3, 0xd5, 0xa5, 0x0a, 0xa6, 0x15, + 0x99, 0xdb, 0x6b, 0x58, 0x0d, 0x5d, 0x91, 0x2c, 0x2a, 0xd3, 0x0b, 0x78, 0xb9, 0x32, 0x5f, 0x59, + 0x36, 0x68, 0xb0, 0xa8, 0x86, 0xac, 0x18, 0xf3, 0xea, 0xe8, 0x26, 0x0c, 0x6e, 0xf2, 0x83, 0x6f, + 0x23, 0x14, 0x49, 0xe3, 0x33, 0x2f, 0xa3, 0x2b, 0x12, 0xc9, 0xa0, 0xc7, 0xae, 0x0c, 0x05, 0xc2, + 0x31, 0x29, 0xf4, 0x05, 0x0b, 0x8e, 0x27, 0xb3, 0xee, 0x33, 0x67, 0x35, 0x61, 0xa6, 0x96, 0xe9, + 0x00, 0x50, 0xce, 0xaa, 0x60, 0x34, 0xc8, 0xd4, 0x2f, 0x99, 0x68, 0x38, 0xbb, 0x39, 0x3a, 0xd0, + 0xc1, 0xed, 0x5a, 0xbb, 0xfc, 0x42, 0x89, 0xc0, 0x44, 0x7c, 0xa0, 0xf1, 0xdc, 0x02, 0xa6, 0x15, + 0xd1, 0x1a, 0xc0, 0x46, 0x9d, 0x88, 0x88, 0x8f, 0xc2, 0x28, 0x2a, 0xf3, 0xf6, 0x5f, 0x52, 0x58, + 0x82, 0x0e, 0x7b, 0x89, 0xc6, 0xa5, 0x58, 0xa3, 0x43, 0x97, 0x52, 0xd5, 0xf5, 0x6a, 0x24, 0x60, + 0xca, 0xad, 0x9c, 0xa5, 0x34, 0xcf, 0x30, 0xd2, 0x4b, 0x89, 0x97, 0x63, 0x41, 0x81, 0xd1, 0x22, + 0xcd, 0xad, 0x8d, 0xb0, 0x5d, 0x26, 0x8b, 0x79, 0xd2, 0xdc, 0x4a, 0x2c, 0x28, 0x4e, 0x8b, 0x95, + 0x63, 0x41, 0x81, 0x6e, 0x99, 0x0d, 0xba, 0x81, 0x48, 0x30, 0x35, 0x96, 0xbf, 0x65, 0x96, 0x38, + 0x4a, 0x7a, 0xcb, 0x08, 0x00, 0x96, 0x44, 0xd0, 0xa7, 0x4d, 0x6e, 0x67, 0x9c, 0xd1, 0x7c, 0xa6, + 0x03, 0xb7, 0x63, 0xd0, 0x6d, 0xcf, 0xef, 0xbc, 0x02, 0x85, 0x8d, 0x2a, 0x53, 0x8a, 0xe5, 0xe8, + 0x0c, 0x96, 0xe6, 0x0d, 0x6a, 0x2c, 0x32, 0xfc, 0xd2, 0x3c, 0x2e, 0x6c, 0x54, 0xe9, 0xd2, 0x77, + 0xee, 0xb5, 0x02, 0xb2, 0xe4, 0xd6, 0x89, 0xc8, 0x6a, 0x91, 0xb9, 0xf4, 0x67, 0x25, 0x52, 0x7a, + 0xe9, 0x2b, 0x10, 0x8e, 0x49, 0x51, 0xba, 0x31, 0x0f, 0x36, 0x99, 0x4f, 0x57, 0xb1, 0x5a, 0x69, + 0xba, 0x99, 0x5c, 0xd8, 0x36, 0x8c, 0xec, 0x84, 0xcd, 0x2d, 0x22, 0x4f, 0x45, 0xa6, 0xae, 0xcb, + 0x89, 0x54, 0x71, 0x53, 0x20, 0xba, 0x41, 0xd4, 0x72, 0xea, 0xa9, 0x83, 0x9c, 0x89, 0x56, 0x6e, + 0xea, 0xc4, 0xb0, 0x49, 0x9b, 0x2e, 0x84, 0x77, 0x79, 0x38, 0x39, 0xa6, 0xb8, 0xcb, 0x59, 0x08, + 0x19, 0x11, 0xe7, 0xf8, 0x42, 0x10, 0x00, 0x2c, 0x89, 0xa8, 0xc1, 0x66, 0x17, 0xd0, 0x89, 0x0e, + 0x83, 0x9d, 0xea, 0x6f, 0x3c, 0xd8, 0xec, 0xc2, 0x89, 0x49, 0xb1, 0x8b, 0xa6, 0xb9, 0xe5, 0x47, + 0xbe, 0x97, 0xb8, 0xe4, 0x4e, 0xe6, 0x5f, 0x34, 0xe5, 0x0c, 0xfc, 0xf4, 0x45, 0x93, 0x85, 0x85, + 0x33, 0xdb, 0xa2, 0x1f, 0xd7, 0x94, 0x91, 0x01, 0x45, 0xe6, 0x8d, 0xa7, 0x72, 0x02, 0x6b, 0xa6, + 0xc3, 0x07, 0xf2, 0x8f, 0x53, 0x20, 0x1c, 0x93, 0x42, 0x35, 0x18, 0x6d, 0x1a, 0x11, 0x67, 0x59, + 0x06, 0x91, 0x1c, 0xbe, 0x20, 0x2b, 0x36, 0x2d, 0x97, 0x10, 0x99, 0x10, 0x9c, 0xa0, 0xc9, 0x2c, + 0xf7, 0xb8, 0xab, 0x1f, 0x4b, 0x30, 0x92, 0x33, 0xd5, 0x19, 0xde, 0x80, 0x7c, 0xaa, 0x05, 0x00, + 0x4b, 0x22, 0x74, 0x34, 0x84, 0x83, 0x9a, 0x1f, 0xb2, 0x3c, 0x3d, 0x79, 0x0a, 0xf6, 0x2c, 0x35, + 0x91, 0x0c, 0xb3, 0x2e, 0x40, 0x38, 0x26, 0x45, 0x4f, 0x72, 0x7a, 0xe1, 0x9d, 0xce, 0x3f, 0xc9, + 0x93, 0xd7, 0x1d, 0x3b, 0xc9, 0xe9, 0x65, 0x57, 0x14, 0x57, 0x9d, 0x8a, 0x0a, 0xce, 0x72, 0x8c, + 0xe4, 0xf4, 0x4b, 0x85, 0x15, 0x4f, 0xf7, 0x4b, 0x81, 0x70, 0x4c, 0x8a, 0x5d, 0xc5, 0x2c, 0x34, + 0xdd, 0xd9, 0x36, 0x57, 0x31, 0x45, 0xc8, 0xb8, 0x8a, 0xb5, 0xd0, 0x75, 0xf6, 0x8f, 0x17, 0xe0, + 0x6c, 0xfb, 0x7d, 0x1b, 0xeb, 0xd0, 0xca, 0xb1, 0xcd, 0x52, 0x42, 0x87, 0xc6, 0x25, 0x3a, 0x31, + 0x56, 0xd7, 0x01, 0x87, 0xaf, 0xc0, 0x84, 0x72, 0x47, 0xac, 0xbb, 0xd5, 0x5d, 0x2d, 0xb1, 0xa8, + 0x0a, 0xcd, 0x53, 0x49, 0x22, 0xe0, 0x74, 0x1d, 0x34, 0x0b, 0x63, 0x46, 0xe1, 0xf2, 0x82, 0x78, + 0xfe, 0xc7, 0xd9, 0x31, 0x4c, 0x30, 0x4e, 0xe2, 0xdb, 0xbf, 0x66, 0xc1, 0xc9, 0x9c, 0x3c, 0xf3, + 0x5d, 0xc7, 0xd3, 0xdd, 0x80, 0xb1, 0xa6, 0x59, 0xb5, 0x43, 0x08, 0x70, 0x23, 0x9b, 0xbd, 0xea, + 0x6b, 0x02, 0x80, 0x93, 0x44, 0xed, 0x5f, 0x29, 0xc0, 0x99, 0xb6, 0xf6, 0xf5, 0x08, 0xc3, 0x89, + 0xcd, 0x46, 0xe8, 0xcc, 0x07, 0xa4, 0x46, 0xbc, 0xc8, 0x75, 0xea, 0x95, 0x26, 0xa9, 0x6a, 0x5a, + 0x50, 0x66, 0xa8, 0x7e, 0x65, 0xa5, 0x32, 0x9b, 0xc6, 0xc0, 0x39, 0x35, 0xd1, 0x12, 0xa0, 0x34, + 0x44, 0xcc, 0x30, 0x7b, 0xe2, 0xa6, 0xe9, 0xe1, 0x8c, 0x1a, 0xe8, 0x65, 0x18, 0x51, 0x76, 0xfb, + 0xda, 0x8c, 0xb3, 0x0b, 0x02, 0xeb, 0x00, 0x6c, 0xe2, 0xa1, 0x4b, 0x3c, 0x6d, 0x92, 0x48, 0xb0, + 0x25, 0x54, 0xa6, 0x63, 0x32, 0x27, 0x92, 0x28, 0xc6, 0x3a, 0xce, 0xdc, 0xe5, 0x3f, 0xfd, 0xf6, + 0xd9, 0x0f, 0xfd, 0xc5, 0xb7, 0xcf, 0x7e, 0xe8, 0xaf, 0xbe, 0x7d, 0xf6, 0x43, 0x3f, 0xb4, 0x7f, + 0xd6, 0xfa, 0xd3, 0xfd, 0xb3, 0xd6, 0x5f, 0xec, 0x9f, 0xb5, 0xfe, 0x6a, 0xff, 0xac, 0xf5, 0xff, + 0xee, 0x9f, 0xb5, 0xbe, 0xfc, 0xb7, 0x67, 0x3f, 0xf4, 0x36, 0x8a, 0x23, 0x54, 0x5f, 0xa4, 0xb3, + 0x73, 0x71, 0xe7, 0xd2, 0x7f, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x60, 0x45, 0x7a, 0xd6, 0xa3, 0x24, + 0x01, 0x00, } func (m *AWSElasticBlockStoreVolumeSource) Marshal() (dAtA []byte, err error) { @@ -16505,6 +16506,20 @@ func (m *PodSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Resources != nil { + { + size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xc2 + } if len(m.ResourceClaims) > 0 { for iNdEx := len(m.ResourceClaims) - 1; iNdEx >= 0; iNdEx-- { { @@ -25084,6 +25099,10 @@ func (m *PodSpec) Size() (n int) { n += 2 + l + sovGenerated(uint64(l)) } } + if m.Resources != nil { + l = m.Resources.Size() + n += 2 + l + sovGenerated(uint64(l)) + } return n } @@ -29347,6 +29366,7 @@ func (this *PodSpec) String() string { `HostUsers:` + valueToStringGenerated(this.HostUsers) + `,`, `SchedulingGates:` + repeatedStringForSchedulingGates + `,`, `ResourceClaims:` + repeatedStringForResourceClaims + `,`, + `Resources:` + strings.Replace(this.Resources.String(), "ResourceRequirements", "ResourceRequirements", 1) + `,`, `}`, }, "") return s @@ -59704,6 +59724,42 @@ func (m *PodSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 40: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resources == nil { + m.Resources = &ResourceRequirements{} + } + if err := m.Resources.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/core/v1/generated.proto b/staging/src/k8s.io/api/core/v1/generated.proto index 6d753fa4b01..08706987c5e 100644 --- a/staging/src/k8s.io/api/core/v1/generated.proto +++ b/staging/src/k8s.io/api/core/v1/generated.proto @@ -4466,6 +4466,21 @@ message PodSpec { // +featureGate=DynamicResourceAllocation // +optional repeated PodResourceClaim resourceClaims = 39; + + // Resources is the total amount of CPU and Memory resources required by all + // containers in the pod. It supports specifying Requests and Limits for + // "cpu" and "memory" resource names only. ResourceClaims are not supported. + // + // This field enables fine-grained control over resource allocation for the + // entire pod, allowing resource sharing among containers in a pod. + // TODO: For beta graduation, expand this comment with a detailed explanation. + // + // This is an alpha field and requires enabling the PodLevelResources feature + // gate. + // + // +featureGate=PodLevelResources + // +optional + optional ResourceRequirements resources = 40; } // PodStatus represents information about the status of a pod. Status may trail the actual diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index d645e0cecba..fb2c1c7453e 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -4087,6 +4087,20 @@ type PodSpec struct { // +featureGate=DynamicResourceAllocation // +optional ResourceClaims []PodResourceClaim `json:"resourceClaims,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,39,rep,name=resourceClaims"` + // Resources is the total amount of CPU and Memory resources required by all + // containers in the pod. It supports specifying Requests and Limits for + // "cpu" and "memory" resource names only. ResourceClaims are not supported. + // + // This field enables fine-grained control over resource allocation for the + // entire pod, allowing resource sharing among containers in a pod. + // TODO: For beta graduation, expand this comment with a detailed explanation. + // + // This is an alpha field and requires enabling the PodLevelResources feature + // gate. + // + // +featureGate=PodLevelResources + // +optional + Resources *ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,40,opt,name=resources"` } // PodResourceClaim references exactly one ResourceClaim, either directly diff --git a/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go index 472945d9045..89ce3d2303b 100644 --- a/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go @@ -1837,6 +1837,7 @@ var map_PodSpec = map[string]string{ "hostUsers": "Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.", "schedulingGates": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.", "resourceClaims": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", + "resources": "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", } func (PodSpec) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/core/v1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/core/v1/zz_generated.deepcopy.go index 1d65a0d7e21..3f669092ef9 100644 --- a/staging/src/k8s.io/api/core/v1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/core/v1/zz_generated.deepcopy.go @@ -4371,6 +4371,11 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(ResourceRequirements) + (*in).DeepCopyInto(*out) + } return } diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.json index a6c89e2e507..433aa567406 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.json @@ -1766,7 +1766,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "updateStrategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.pb index b75325807c5..b9aaa8c0cfd 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.yaml index b2fe097d004..cf27e24793b 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.yaml @@ -864,6 +864,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.json index f44ffb4bb93..6e68ee71913 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "strategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.pb index ddaa4b71e08..e51e1c32335 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.yaml index ec8f334121b..89522519566 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.yaml @@ -872,6 +872,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.json index 97d93e688b3..a9dfb08a322 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.json @@ -1768,7 +1768,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } } }, diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.pb index 957822aeb54..a5ff7dd066d 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.yaml index 7db2694f606..42fba0088fd 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.yaml @@ -864,6 +864,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.json index 8130519907a..d72a8ead261 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "volumeClaimTemplates": [ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.pb index 3137a439ffe..070fc0a6c6e 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.yaml index 7b747e00f90..ce0f847f4c9 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.yaml @@ -872,6 +872,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.json index 9cedd55ad0a..715007f246f 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "strategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.pb index c8b30c7b194..4ea65f9027e 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.yaml index 2d2e5836dc8..10b6d3d87cf 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.yaml @@ -874,6 +874,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.json index f382fc62f79..78802d9ddc4 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "volumeClaimTemplates": [ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.pb index ca2576f57b2..6525f9a3d8b 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.yaml index d85954d2f3d..aa3096aeec2 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.yaml @@ -872,6 +872,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.json index bf085d4e203..4e74dd3716d 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.json @@ -1766,7 +1766,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "updateStrategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.pb index 6a0fd96123f..f7f0a63e26c 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.yaml index fc59afcd925..040450a2f10 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.yaml @@ -864,6 +864,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.json index 2081c9ac26d..4b0e0e3e502 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "strategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.pb index 5a95130c796..f46ae2c4c8d 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.yaml index 947b24fb4d4..0ae783fea37 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.yaml @@ -872,6 +872,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.json index 677f0d6fd67..ccd899cc259 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.json @@ -1768,7 +1768,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } } }, diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.pb index 48cb5e6f60d..b069aa0705a 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.yaml index ebb64e03064..4e4211c1da7 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.yaml @@ -864,6 +864,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.json b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.json index 177b5b3c20a..d3f43b082b4 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "volumeClaimTemplates": [ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.pb b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.pb index a17feb480f8..f4bd66e9e72 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.yaml index 2eff78ed0cd..9f6105ed55b 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.yaml @@ -872,6 +872,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json index a562c92d1fe..cb46e57e445 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json @@ -1850,7 +1850,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "ttlSecondsAfterFinished": 8, diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb index 012d2325a99..ff6c0f89782 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb and b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.yaml index 7b5008f31ff..773dce02c66 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.yaml @@ -924,6 +924,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json index 661fbd423b8..ce6f5c10411 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json @@ -1801,7 +1801,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "ttlSecondsAfterFinished": 8, diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb index 9f1a340844b..0ea948f1f72 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb and b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml index 62f868d0c13..b5c1219e2b0 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml @@ -888,6 +888,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json index ec951ff67bb..4b1b0704bbb 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json @@ -1850,7 +1850,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "ttlSecondsAfterFinished": 8, diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb index 2e9cc214316..abe891dbe0f 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb and b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml index 542bc2ead14..2a6e4e78b6e 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml @@ -924,6 +924,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json b/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json index 12b91a85fcf..aedd4a22dc0 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json +++ b/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json @@ -1708,7 +1708,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } }, "status": { "phase": "phaseValue", diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb b/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb index 7be883eeac6..9b44310ba34 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb and b/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.yaml b/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.yaml index a76207e05eb..31532e145a2 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.yaml @@ -820,6 +820,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.json b/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.json index 072f216ed86..7ab8ec402b7 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.json +++ b/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.json @@ -1751,7 +1751,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } } } \ No newline at end of file diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.pb b/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.pb index 3cc9b29777c..8efa534315a 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.pb and b/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.yaml b/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.yaml index 0689a77708a..8c3494faa7a 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.yaml @@ -853,6 +853,14 @@ template: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.json b/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.json index 23398afceb2..e267b1ef2cd 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.json +++ b/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.json @@ -1757,7 +1757,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } } }, diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.pb b/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.pb index 241b684dcd1..1610a0c31d2 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.pb and b/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.yaml b/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.yaml index 73a63905eb7..ffa315e641d 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.yaml @@ -858,6 +858,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.json b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.json index 61c555e463a..df154fa098f 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.json @@ -1766,7 +1766,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "updateStrategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.pb b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.pb index dbd68accd8f..2155f9e55ec 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.yaml index 894d06f2139..ca7ee893736 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.yaml @@ -864,6 +864,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.json b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.json index fc26c1a9e61..5a6cbb46e23 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.json +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.json @@ -1767,7 +1767,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } }, "strategy": { diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.pb b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.pb index ecd47966df0..480fc47fdb9 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.pb and b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.yaml b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.yaml index 75a96cd3947..b3f5eb3209a 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.yaml @@ -874,6 +874,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.json b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.json index 790f43bbe3e..32e8468ec42 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.json +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.json @@ -1768,7 +1768,21 @@ "resourceClaimName": "resourceClaimNameValue", "resourceClaimTemplateName": "resourceClaimTemplateNameValue" } - ] + ], + "resources": { + "limits": { + "limitsKey": "0" + }, + "requests": { + "requestsKey": "0" + }, + "claims": [ + { + "name": "nameValue", + "request": "requestValue" + } + ] + } } } }, diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.pb b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.pb index b09761515cd..1e4a88f2df5 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.pb and b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.yaml b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.yaml index 43b01319e23..75188bb3f4a 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.yaml @@ -864,6 +864,14 @@ spec: - name: nameValue resourceClaimName: resourceClaimNameValue resourceClaimTemplateName: resourceClaimTemplateNameValue + resources: + claims: + - name: nameValue + request: requestValue + limits: + limitsKey: "0" + requests: + requestsKey: "0" restartPolicy: restartPolicyValue runtimeClassName: runtimeClassNameValue schedulerName: schedulerNameValue diff --git a/staging/src/k8s.io/client-go/applyconfigurations/core/v1/podspec.go b/staging/src/k8s.io/client-go/applyconfigurations/core/v1/podspec.go index 8134e044f19..96f6eb94b1c 100644 --- a/staging/src/k8s.io/client-go/applyconfigurations/core/v1/podspec.go +++ b/staging/src/k8s.io/client-go/applyconfigurations/core/v1/podspec.go @@ -64,6 +64,7 @@ type PodSpecApplyConfiguration struct { HostUsers *bool `json:"hostUsers,omitempty"` SchedulingGates []PodSchedulingGateApplyConfiguration `json:"schedulingGates,omitempty"` ResourceClaims []PodResourceClaimApplyConfiguration `json:"resourceClaims,omitempty"` + Resources *ResourceRequirementsApplyConfiguration `json:"resources,omitempty"` } // PodSpecApplyConfiguration constructs a declarative configuration of the PodSpec type for use with @@ -444,3 +445,11 @@ func (b *PodSpecApplyConfiguration) WithResourceClaims(values ...*PodResourceCla } return b } + +// WithResources sets the Resources field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Resources field is set to the value of the last call. +func (b *PodSpecApplyConfiguration) WithResources(value *ResourceRequirementsApplyConfiguration) *PodSpecApplyConfiguration { + b.Resources = value + return b +} diff --git a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go index 6a3f688ca62..cd9fcd98b7b 100644 --- a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go +++ b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go @@ -7167,6 +7167,9 @@ var schemaYAML = typed.YAMLObject(`types: elementRelationship: associative keys: - name + - name: resources + type: + namedType: io.k8s.api.core.v1.ResourceRequirements - name: restartPolicy type: scalar: string diff --git a/staging/src/k8s.io/component-helpers/resource/helpers.go b/staging/src/k8s.io/component-helpers/resource/helpers.go index a8b252cfdd4..3bdcef6e816 100644 --- a/staging/src/k8s.io/component-helpers/resource/helpers.go +++ b/staging/src/k8s.io/component-helpers/resource/helpers.go @@ -18,6 +18,7 @@ package resource import ( v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" ) // ContainerType signifies container type @@ -46,15 +47,101 @@ type PodResourcesOptions struct { // NonMissingContainerRequests if provided will replace any missing container level requests for the specified resources // with the given values. If the requests for those resources are explicitly set, even if zero, they will not be modified. NonMissingContainerRequests v1.ResourceList + // SkipPodLevelResources controls whether pod-level resources should be skipped + // from the calculation. If pod-level resources are not set in PodSpec, + // pod-level resources will always be skipped. + SkipPodLevelResources bool } -// PodRequests computes the pod requests per the PodResourcesOptions supplied. If PodResourcesOptions is nil, then -// the requests are returned including pod overhead. The computation is part of the API and must be reviewed -// as an API change. +var supportedPodLevelResources = sets.New(v1.ResourceCPU, v1.ResourceMemory) + +func SupportedPodLevelResources() sets.Set[v1.ResourceName] { + return supportedPodLevelResources +} + +// IsSupportedPodLevelResources checks if a given resource is supported by pod-level +// resource management through the PodLevelResources feature. Returns true if +// the resource is supported. +func IsSupportedPodLevelResource(name v1.ResourceName) bool { + return supportedPodLevelResources.Has(name) +} + +// IsPodLevelResourcesSet check if PodLevelResources pod-level resources are set. +// It returns true if either the Requests or Limits maps are non-empty. +func IsPodLevelResourcesSet(pod *v1.Pod) bool { + if pod.Spec.Resources == nil { + return false + } + + if (len(pod.Spec.Resources.Requests) + len(pod.Spec.Resources.Limits)) == 0 { + return false + } + + for resourceName := range pod.Spec.Resources.Requests { + if IsSupportedPodLevelResource(resourceName) { + return true + } + } + + for resourceName := range pod.Spec.Resources.Limits { + if IsSupportedPodLevelResource(resourceName) { + return true + } + } + + return false +} + +// IsPodLevelRequestsSet checks if pod-level requests are set. It returns true if +// Requests map is non-empty. +func IsPodLevelRequestsSet(pod *v1.Pod) bool { + if pod.Spec.Resources == nil { + return false + } + + if len(pod.Spec.Resources.Requests) == 0 { + return false + } + + for resourceName := range pod.Spec.Resources.Requests { + if IsSupportedPodLevelResource(resourceName) { + return true + } + } + + return false +} + +// PodRequests computes the total pod requests per the PodResourcesOptions supplied. +// If PodResourcesOptions is nil, then the requests are returned including pod overhead. +// If the PodLevelResources feature is enabled AND the pod-level resources are set, +// those pod-level values are used in calculating Pod Requests. +// The computation is part of the API and must be reviewed as an API change. func PodRequests(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList { + reqs := AggregateContainerRequests(pod, opts) + if !opts.SkipPodLevelResources && IsPodLevelRequestsSet(pod) { + for resourceName, quantity := range pod.Spec.Resources.Requests { + if IsSupportedPodLevelResource(resourceName) { + reqs[resourceName] = quantity + } + } + } + + // Add overhead for running a pod to the sum of requests if requested: + if !opts.ExcludeOverhead && pod.Spec.Overhead != nil { + addResourceList(reqs, pod.Spec.Overhead) + } + + return reqs +} + +// AggregateContainerRequests computes the total resource requests of all the containers +// in a pod. This computation folows the formula defined in the KEP for sidecar +// containers. See https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/753-sidecar-containers#resources-calculation-for-scheduling-and-pod-admission +// for more details. +func AggregateContainerRequests(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList { // attempt to reuse the maps if passed, or allocate otherwise reqs := reuseOrClearResourceList(opts.Reuse) - var containerStatuses map[string]*v1.ContainerStatus if opts.UseStatusResources { containerStatuses = make(map[string]*v1.ContainerStatus, len(pod.Status.ContainerStatuses)) @@ -124,12 +211,6 @@ func PodRequests(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList { } maxResourceList(reqs, initContainerReqs) - - // Add overhead for running a pod to the sum of requests if requested: - if !opts.ExcludeOverhead && pod.Spec.Overhead != nil { - addResourceList(reqs, pod.Spec.Overhead) - } - return reqs } @@ -155,8 +236,35 @@ func applyNonMissing(reqs v1.ResourceList, nonMissing v1.ResourceList) v1.Resour // as an API change. func PodLimits(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList { // attempt to reuse the maps if passed, or allocate otherwise - limits := reuseOrClearResourceList(opts.Reuse) + limits := AggregateContainerLimits(pod, opts) + if !opts.SkipPodLevelResources && IsPodLevelResourcesSet(pod) { + for resourceName, quantity := range pod.Spec.Resources.Limits { + if IsSupportedPodLevelResource(resourceName) { + limits[resourceName] = quantity + } + } + } + // Add overhead to non-zero limits if requested: + if !opts.ExcludeOverhead && pod.Spec.Overhead != nil { + for name, quantity := range pod.Spec.Overhead { + if value, ok := limits[name]; ok && !value.IsZero() { + value.Add(quantity) + limits[name] = value + } + } + } + + return limits +} + +// AggregateContainerLimits computes the aggregated resource limits of all the containers +// in a pod. This computation follows the formula defined in the KEP for sidecar +// containers. See https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/753-sidecar-containers#resources-calculation-for-scheduling-and-pod-admission +// for more details. +func AggregateContainerLimits(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList { + // attempt to reuse the maps if passed, or allocate otherwise + limits := reuseOrClearResourceList(opts.Reuse) var containerStatuses map[string]*v1.ContainerStatus if opts.UseStatusResources { containerStatuses = make(map[string]*v1.ContainerStatus, len(pod.Status.ContainerStatuses)) @@ -216,17 +324,6 @@ func PodLimits(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList { } maxResourceList(limits, initContainerLimits) - - // Add overhead to non-zero limits if requested: - if !opts.ExcludeOverhead && pod.Spec.Overhead != nil { - for name, quantity := range pod.Spec.Overhead { - if value, ok := limits[name]; ok && !value.IsZero() { - value.Add(quantity) - limits[name] = value - } - } - } - return limits } diff --git a/staging/src/k8s.io/component-helpers/resource/helpers_test.go b/staging/src/k8s.io/component-helpers/resource/helpers_test.go index f2d984cf65d..e146a311971 100644 --- a/staging/src/k8s.io/component-helpers/resource/helpers_test.go +++ b/staging/src/k8s.io/component-helpers/resource/helpers_test.go @@ -1228,10 +1228,529 @@ func TestPodResourceLimits(t *testing.T) { } } +func TestIsPodLevelResourcesSet(t *testing.T) { + testCases := []struct { + name string + podResources *v1.ResourceRequirements + expected bool + }{ + { + name: "nil resources struct", + expected: false, + }, + { + name: "empty resources struct", + podResources: &v1.ResourceRequirements{}, + expected: false, + }, + { + name: "only unsupported resource requests set", + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceEphemeralStorage: resource.MustParse("1Mi")}, + }, + expected: false, + }, + { + name: "only unsupported resource limits set", + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceEphemeralStorage: resource.MustParse("1Mi")}, + }, + expected: false, + }, + { + name: "unsupported and suported resources requests set", + podResources: &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceEphemeralStorage: resource.MustParse("1Mi"), + v1.ResourceCPU: resource.MustParse("1m"), + }, + }, + expected: true, + }, + { + name: "unsupported and suported resources limits set", + podResources: &v1.ResourceRequirements{ + Limits: v1.ResourceList{ + v1.ResourceEphemeralStorage: resource.MustParse("1Mi"), + v1.ResourceCPU: resource.MustParse("1m"), + }, + }, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + testPod := &v1.Pod{Spec: v1.PodSpec{Resources: tc.podResources}} + if got := IsPodLevelResourcesSet(testPod); got != tc.expected { + t.Errorf("got=%t, want=%t", got, tc.expected) + } + }) + } + +} + +func TestPodLevelResourceRequests(t *testing.T) { + restartAlways := v1.ContainerRestartPolicyAlways + testCases := []struct { + name string + opts PodResourcesOptions + podResources v1.ResourceRequirements + overhead v1.ResourceList + initContainers []v1.Container + containers []v1.Container + expectedRequests v1.ResourceList + }{ + { + name: "nil", + expectedRequests: v1.ResourceList{}, + }, + { + name: "pod level memory resource with SkipPodLevelResources true", + podResources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Mi")}}, + opts: PodResourcesOptions{SkipPodLevelResources: true}, + expectedRequests: v1.ResourceList{}, + }, + { + name: "pod level memory resource with SkipPodLevelResources false", + podResources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Mi")}}, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Mi")}, + }, + { + name: "pod level memory and container level cpu resources with SkipPodLevelResources false", + podResources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Mi")}}, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("2m")}}, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Mi"), v1.ResourceCPU: resource.MustParse("2m")}, + }, + { + name: "pod level unsupported resources set at both pod-level and container-level with SkipPodLevelResources false", + podResources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("2Mi")}}, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("3Mi")}}, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("3Mi")}, + }, + { + name: "pod level unsupported resources set at pod-level with SkipPodLevelResources false", + podResources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("2Mi")}}, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("3Mi")}}, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("3Mi")}, + }, + { + name: "only container level resources set with SkipPodLevelResources false", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("3Mi"), v1.ResourceCPU: resource.MustParse("2m")}, + }, + { + name: "both container-level and pod-level resources set with SkipPodLevelResources false", + podResources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("6Mi"), + v1.ResourceCPU: resource.MustParse("8m"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("6Mi"), v1.ResourceCPU: resource.MustParse("8m")}, + }, + { + name: "container-level resources and init container set with SkipPodLevelResources false", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("5Mi"), + v1.ResourceCPU: resource.MustParse("4m"), + }, + }, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("5Mi"), v1.ResourceCPU: resource.MustParse("4m")}, + }, + { + name: "container-level resources and init container set with SkipPodLevelResources false", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("5Mi"), + v1.ResourceCPU: resource.MustParse("4m"), + }, + }, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: true}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("5Mi"), v1.ResourceCPU: resource.MustParse("4m")}, + }, + { + name: "container-level resources and sidecar container set with SkipPodLevelResources false", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("5Mi"), + v1.ResourceCPU: resource.MustParse("4m"), + }, + }, + RestartPolicy: &restartAlways, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("8Mi"), v1.ResourceCPU: resource.MustParse("6m")}, + }, + { + name: "container-level resources, init and sidecar container set with SkipPodLevelResources false", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("5Mi"), + v1.ResourceCPU: resource.MustParse("4m"), + }, + }, + RestartPolicy: &restartAlways, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("6Mi"), + v1.ResourceCPU: resource.MustParse("8m"), + }, + }, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("11Mi"), v1.ResourceCPU: resource.MustParse("12m")}, + }, + { + name: "pod-level resources, container-level resources, init and sidecar container set with SkipPodLevelResources false", + podResources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("15Mi"), + v1.ResourceCPU: resource.MustParse("18m"), + }, + }, + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("3Mi"), + v1.ResourceCPU: resource.MustParse("2m"), + }, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("5Mi"), + v1.ResourceCPU: resource.MustParse("4m"), + }, + }, + RestartPolicy: &restartAlways, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceMemory: resource.MustParse("6Mi"), + v1.ResourceCPU: resource.MustParse("8m"), + }, + }, + }, + }, + opts: PodResourcesOptions{SkipPodLevelResources: false}, + expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("15Mi"), v1.ResourceCPU: resource.MustParse("18m")}, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + podReqs := PodRequests(getPodLevelResourcesPod(tc.podResources, tc.overhead, tc.containers, tc.initContainers), tc.opts) + if diff := cmp.Diff(podReqs, tc.expectedRequests); diff != "" { + t.Errorf("got=%v, want=%v, diff=%s", podReqs, tc.expectedRequests, diff) + } + }) + } +} + +func TestAggregateContainerRequestsAndLimits(t *testing.T) { + restartAlways := v1.ContainerRestartPolicyAlways + cases := []struct { + containers []v1.Container + initContainers []v1.Container + name string + expectedRequests v1.ResourceList + expectedLimits v1.ResourceList + }{ + { + name: "one container with limits", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + expectedRequests: v1.ResourceList{}, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"), + }, + }, + { + name: "two containers with limits", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + expectedRequests: v1.ResourceList{}, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("18"), + }, + }, + { + name: "one container with requests", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"), + }, + expectedLimits: v1.ResourceList{}, + }, + { + name: "two containers with requests", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("18"), + }, + expectedLimits: v1.ResourceList{}, + }, + { + name: "regular and init containers with requests", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"), + }, + expectedLimits: v1.ResourceList{}, + }, + { + name: "regular, init and sidecar containers with requests", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("8")}, + }, + RestartPolicy: &restartAlways, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("6")}, + }, + }, + }, + expectedRequests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("17"), + }, + expectedLimits: v1.ResourceList{}, + }, + { + name: "regular and init containers with limits", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + expectedRequests: v1.ResourceList{}, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"), + }, + }, + { + name: "regular, init and sidecar containers with limits", + containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("9")}, + }, + }, + }, + initContainers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("8")}, + }, + RestartPolicy: &restartAlways, + }, + { + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{v1.ResourceName(v1.ResourceCPU): resource.MustParse("6")}, + }, + }, + }, + expectedRequests: v1.ResourceList{}, + expectedLimits: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse("17"), + }, + }, + } + + for idx, tc := range cases { + testPod := &v1.Pod{Spec: v1.PodSpec{Containers: tc.containers, InitContainers: tc.initContainers}} + resRequests := AggregateContainerRequests(testPod, PodResourcesOptions{}) + resLimits := AggregateContainerLimits(testPod, PodResourcesOptions{}) + + if !equality.Semantic.DeepEqual(tc.expectedRequests, resRequests) { + t.Errorf("test case failure[%d]: %v, requests:\n expected:\t%v\ngot\t\t%v", idx, tc.name, tc.expectedRequests, resRequests) + } + + if !equality.Semantic.DeepEqual(tc.expectedLimits, resLimits) { + t.Errorf("test case failure[%d]: %v, limits:\n expected:\t%v\ngot\t\t%v", idx, tc.name, tc.expectedLimits, resLimits) + } + } +} + type podResources struct { cpuRequest, cpuLimit, memoryRequest, memoryLimit, cpuOverhead, memoryOverhead string } +func getPodLevelResourcesPod(podResources v1.ResourceRequirements, overhead v1.ResourceList, containers, initContainers []v1.Container) *v1.Pod { + return &v1.Pod{ + Spec: v1.PodSpec{ + Resources: &podResources, + Containers: containers, + InitContainers: initContainers, + Overhead: overhead, + }, + } +} + +// TODO(ndixita): refactor to re-use getPodResourcesPod() func getPod(cname string, resources podResources) *v1.Pod { r := v1.ResourceRequirements{ Limits: make(v1.ResourceList), diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index 1e6acd75e13..75b1662e074 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -838,6 +838,11 @@ func describePod(pod *corev1.Pod, events *corev1.EventList) (string, error) { w.Write(LEVEL_0, "NominatedNodeName:\t%s\n", pod.Status.NominatedNodeName) } + if pod.Spec.Resources != nil { + w.Write(LEVEL_0, "Resources:\n") + describeResources(pod.Spec.Resources, w, LEVEL_1) + } + if len(pod.Spec.InitContainers) > 0 { describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), w, "") } @@ -1800,7 +1805,7 @@ func describeContainers(label string, containers []corev1.Container, containerSt if ok { describeContainerState(status, w) } - describeContainerResource(container, w) + describeResources(&container.Resources, w, LEVEL_2) describeContainerProbe(container, w) if len(container.EnvFrom) > 0 { describeContainerEnvFrom(container, resolverFn, w) @@ -1886,22 +1891,25 @@ func describeContainerCommand(container corev1.Container, w PrefixWriter) { } } -func describeContainerResource(container corev1.Container, w PrefixWriter) { - resources := container.Resources +func describeResources(resources *corev1.ResourceRequirements, w PrefixWriter, level int) { + if resources == nil { + return + } + if len(resources.Limits) > 0 { - w.Write(LEVEL_2, "Limits:\n") + w.Write(level, "Limits:\n") } for _, name := range SortedResourceNames(resources.Limits) { quantity := resources.Limits[name] - w.Write(LEVEL_3, "%s:\t%s\n", name, quantity.String()) + w.Write(level+1, "%s:\t%s\n", name, quantity.String()) } if len(resources.Requests) > 0 { - w.Write(LEVEL_2, "Requests:\n") + w.Write(level, "Requests:\n") } for _, name := range SortedResourceNames(resources.Requests) { quantity := resources.Requests[name] - w.Write(LEVEL_3, "%s:\t%s\n", name, quantity.String()) + w.Write(level+1, "%s:\t%s\n", name, quantity.String()) } } diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go index 27930c0846e..946ece04b68 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -1179,6 +1179,71 @@ func VerifyDatesInOrder( } } +func TestDescribeResources(t *testing.T) { + testCases := []struct { + resources *corev1.ResourceRequirements + expectedElements map[string]int + }{ + { + resources: &corev1.ResourceRequirements{}, + expectedElements: map[string]int{}, + }, + { + resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1000"), + corev1.ResourceMemory: resource.MustParse("100Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1000"), + corev1.ResourceMemory: resource.MustParse("100Mi"), + }, + }, + expectedElements: map[string]int{"cpu": 2, "memory": 2, "Requests": 1, "Limits": 1, "1k": 2, "100Mi": 2}, + }, + { + resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1000"), + corev1.ResourceMemory: resource.MustParse("100Mi"), + }, + }, + expectedElements: map[string]int{"cpu": 1, "memory": 1, "Limits": 1, "1k": 1, "100Mi": 1}, + }, + { + resources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1000"), + corev1.ResourceMemory: resource.MustParse("100Mi"), + }, + }, + expectedElements: map[string]int{"cpu": 1, "memory": 1, "Requests": 1, "1k": 1, "100Mi": 1}, + }, + } + + for i, testCase := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + out := new(bytes.Buffer) + writer := NewPrefixWriter(out) + describeResources(testCase.resources, writer, LEVEL_1) + output := out.String() + gotElements := make(map[string]int) + for key, val := range testCase.expectedElements { + count := strings.Count(output, key) + if count == 0 { + t.Errorf("expected to find %q in output: %q", val, output) + continue + } + gotElements[key] = count + } + + if !reflect.DeepEqual(gotElements, testCase.expectedElements) { + t.Errorf("Expected %v, got %v in output string: %q", testCase.expectedElements, gotElements, output) + } + }) + } +} + func TestDescribeContainers(t *testing.T) { trueVal := true testCases := []struct { diff --git a/staging/src/k8s.io/kubectl/pkg/util/qos/qos.go b/staging/src/k8s.io/kubectl/pkg/util/qos/qos.go index 68b1b9072a9..1b102a0fe56 100644 --- a/staging/src/k8s.io/kubectl/pkg/util/qos/qos.go +++ b/staging/src/k8s.io/kubectl/pkg/util/qos/qos.go @@ -37,6 +37,45 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass { return ComputePodQOS(pod) } +// zeroQuantity represents a resource.Quantity with value "0", used as a baseline +// for resource comparisons. +var zeroQuantity = resource.MustParse("0") + +// processResourceList adds non-zero quantities for supported QoS compute resources +// quantities from newList to list. +func processResourceList(list, newList core.ResourceList) { + for name, quantity := range newList { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + delta := quantity.DeepCopy() + if _, exists := list[name]; !exists { + list[name] = delta + } else { + delta.Add(list[name]) + list[name] = delta + } + } + } +} + +// getQOSResources returns a set of resource names from the provided resource list that: +// 1. Are supported QoS compute resources +// 2. Have quantities greater than zero +func getQOSResources(list core.ResourceList) sets.Set[string] { + qosResources := sets.New[string]() + for name, quantity := range list { + if !isSupportedQoSComputeResource(name) { + continue + } + if quantity.Cmp(zeroQuantity) == 1 { + qosResources.Insert(string(name)) + } + } + return qosResources +} + // ComputePodQOS evaluates the list of containers to determine a pod's QoS class. This function is more // expensive than GetPodQOS which should be used for pods having a non-empty .Status.QOSClass. // A pod is besteffort if none of its containers have specified any requests or limits. @@ -45,50 +84,38 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass { func ComputePodQOS(pod *core.Pod) core.PodQOSClass { requests := core.ResourceList{} limits := core.ResourceList{} - zeroQuantity := resource.MustParse("0") isGuaranteed := true - // note, ephemeral containers are not considered for QoS as they cannot define resources - allContainers := []core.Container{} - allContainers = append(allContainers, pod.Spec.Containers...) - allContainers = append(allContainers, pod.Spec.InitContainers...) - for _, container := range allContainers { - // process requests - for name, quantity := range container.Resources.Requests { - if !isSupportedQoSComputeResource(name) { - continue - } - if quantity.Cmp(zeroQuantity) == 1 { - delta := quantity.DeepCopy() - if _, exists := requests[name]; !exists { - requests[name] = delta - } else { - delta.Add(requests[name]) - requests[name] = delta - } - } - } - // process limits - qosLimitsFound := sets.NewString() - for name, quantity := range container.Resources.Limits { - if !isSupportedQoSComputeResource(name) { - continue - } - if quantity.Cmp(zeroQuantity) == 1 { - qosLimitsFound.Insert(string(name)) - delta := quantity.DeepCopy() - if _, exists := limits[name]; !exists { - limits[name] = delta - } else { - delta.Add(limits[name]) - limits[name] = delta - } - } + if pod.Spec.Resources != nil { + if pod.Spec.Resources.Requests != nil { + // process requests + processResourceList(requests, pod.Spec.Resources.Requests) } - if !qosLimitsFound.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { - isGuaranteed = false + if pod.Spec.Resources.Limits != nil { + // process limits + processResourceList(limits, pod.Spec.Resources.Limits) + qosLimitResources := getQOSResources(pod.Spec.Resources.Limits) + if !qosLimitResources.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { + isGuaranteed = false + } + } + } else { + // note, ephemeral containers are not considered for QoS as they cannot define resources + allContainers := []core.Container{} + allContainers = append(allContainers, pod.Spec.Containers...) + allContainers = append(allContainers, pod.Spec.InitContainers...) + for _, container := range allContainers { + // process requests + processResourceList(requests, container.Resources.Requests) + // process limits + processResourceList(limits, container.Resources.Limits) + qosLimitResources := getQOSResources(container.Resources.Limits) + if !qosLimitResources.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { + isGuaranteed = false + } } } + if len(requests) == 0 && len(limits) == 0 { return core.PodQOSBestEffort } diff --git a/test/e2e/common/node/pod_level_resources.go b/test/e2e/common/node/pod_level_resources.go new file mode 100644 index 00000000000..8dbe18b1d8b --- /dev/null +++ b/test/e2e/common/node/pod_level_resources.go @@ -0,0 +1,408 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package node + +import ( + "context" + "fmt" + "strconv" + "strings" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + kubecm "k8s.io/kubernetes/pkg/kubelet/cm" + "k8s.io/kubernetes/test/e2e/feature" + "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" + e2epod "k8s.io/kubernetes/test/e2e/framework/pod" + e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" + imageutils "k8s.io/kubernetes/test/utils/image" + admissionapi "k8s.io/pod-security-admission/api" +) + +const ( + cgroupv2CPUWeight string = "cpu.weight" + cgroupv2CPULimit string = "cpu.max" + cgroupv2MemLimit string = "memory.max" + cgroupFsPath string = "/sys/fs/cgroup" + CPUPeriod string = "100000" + mountPath string = "/sysfscgroup" +) + +var ( + cmd = []string{"/bin/sh", "-c", "sleep 1d"} +) + +var _ = SIGDescribe("Pod Level Resources", framework.WithSerial(), feature.PodLevelResources, "[NodeAlphaFeature:PodLevelResources]", func() { + f := framework.NewDefaultFramework("pod-level-resources-tests") + f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged + + ginkgo.BeforeEach(func(ctx context.Context) { + _, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) + framework.ExpectNoError(err) + + if framework.NodeOSDistroIs("windows") { + e2eskipper.Skipf("not supported on windows -- skipping") + } + + // skip the test on nodes with cgroupv2 not enabled. + if !isCgroupv2Node(f, ctx) { + e2eskipper.Skipf("not supported on cgroupv1 -- skipping") + } + }) + podLevelResourcesTests(f) +}) + +// isCgroupv2Node creates a small pod and check if it is running on a node +// with cgroupv2 enabled. +// TODO: refactor to mark this test with cgroupv2 label, and rather check +// the label in the test job, to tun this test on a node with cgroupv2. +func isCgroupv2Node(f *framework.Framework, ctx context.Context) bool { + podClient := e2epod.NewPodClient(f) + cgroupv2Testpod := &v1.Pod{ + ObjectMeta: makeObjectMetadata("cgroupv2-check", f.Namespace.Name), + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "cgroupv2-check", + Image: imageutils.GetE2EImage(imageutils.BusyBox), + Command: cmd, + Resources: getResourceRequirements(&resourceInfo{CPULim: "1m", MemReq: "1Mi"}), + }, + }, + }, + } + + pod := podClient.CreateSync(ctx, cgroupv2Testpod) + defer func() { + framework.Logf("Deleting %q pod", cgroupv2Testpod.Name) + delErr := e2epod.DeletePodWithWait(ctx, f.ClientSet, pod) + framework.ExpectNoError(delErr, "failed to delete pod %s", delErr) + }() + + return e2epod.IsPodOnCgroupv2Node(f, pod) +} + +func makeObjectMetadata(name, namespace string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: "testpod", Namespace: namespace, + Labels: map[string]string{"time": strconv.Itoa(time.Now().Nanosecond())}, + } +} + +type containerInfo struct { + Name string + Resources *resourceInfo +} +type resourceInfo struct { + CPUReq string + CPULim string + MemReq string + MemLim string +} + +func makeContainer(info containerInfo) v1.Container { + cmd := []string{"/bin/sh", "-c", "sleep 1d"} + res := getResourceRequirements(info.Resources) + return v1.Container{ + Name: info.Name, + Command: cmd, + Resources: res, + Image: imageutils.GetE2EImage(imageutils.BusyBox), + VolumeMounts: []v1.VolumeMount{ + { + Name: "sysfscgroup", + MountPath: mountPath, + }, + }, + } +} + +func getResourceRequirements(info *resourceInfo) v1.ResourceRequirements { + var res v1.ResourceRequirements + if info != nil { + if info.CPUReq != "" || info.MemReq != "" { + res.Requests = make(v1.ResourceList) + } + if info.CPUReq != "" { + res.Requests[v1.ResourceCPU] = resource.MustParse(info.CPUReq) + } + if info.MemReq != "" { + res.Requests[v1.ResourceMemory] = resource.MustParse(info.MemReq) + } + + if info.CPULim != "" || info.MemLim != "" { + res.Limits = make(v1.ResourceList) + } + if info.CPULim != "" { + res.Limits[v1.ResourceCPU] = resource.MustParse(info.CPULim) + } + if info.MemLim != "" { + res.Limits[v1.ResourceMemory] = resource.MustParse(info.MemLim) + } + } + return res +} + +func makePod(metadata *metav1.ObjectMeta, podResources *resourceInfo, containers []containerInfo) *v1.Pod { + var testContainers []v1.Container + for _, container := range containers { + testContainers = append(testContainers, makeContainer(container)) + } + + pod := &v1.Pod{ + ObjectMeta: *metadata, + + Spec: v1.PodSpec{ + Containers: testContainers, + Volumes: []v1.Volume{ + { + Name: "sysfscgroup", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{Path: cgroupFsPath}, + }, + }, + }, + }, + } + + if podResources != nil { + res := getResourceRequirements(podResources) + pod.Spec.Resources = &res + } + + return pod +} + +func verifyPodResources(gotPod v1.Pod, inputInfo, expectedInfo *resourceInfo) { + ginkgo.GinkgoHelper() + var expectedResources *v1.ResourceRequirements + // expectedResources will be nil if pod-level resources are not set in the test + // case input. + if inputInfo != nil { + resourceInfo := getResourceRequirements(expectedInfo) + expectedResources = &resourceInfo + } + gomega.Expect(expectedResources).To(gomega.Equal(gotPod.Spec.Resources)) +} + +func verifyQoS(gotPod v1.Pod, expectedQoS v1.PodQOSClass) { + ginkgo.GinkgoHelper() + gomega.Expect(expectedQoS).To(gomega.Equal(gotPod.Status.QOSClass)) +} + +// TODO(ndixita): dedup the conversion logic in pod resize test and move to helpers/utils. +func verifyPodCgroups(ctx context.Context, f *framework.Framework, pod *v1.Pod, info *resourceInfo) error { + ginkgo.GinkgoHelper() + cmd := fmt.Sprintf("find %s -name '*%s*'", mountPath, strings.ReplaceAll(string(pod.UID), "-", "_")) + framework.Logf("Namespace %s Pod %s - looking for Pod cgroup directory path: %q", f.Namespace, pod.Name, cmd) + podCgPath, stderr, err := e2epod.ExecCommandInContainerWithFullOutput(f, pod.Name, pod.Spec.Containers[0].Name, []string{"/bin/sh", "-c", cmd}...) + if err != nil || len(stderr) > 0 { + return fmt.Errorf("encountered error while running command: %q, \nerr: %w \nstdErr: %q", cmd, err, stderr) + } + + expectedResources := getResourceRequirements(info) + cpuWeightCgPath := fmt.Sprintf("%s/%s", podCgPath, cgroupv2CPUWeight) + expectedCPUShares := int64(kubecm.MilliCPUToShares(expectedResources.Requests.Cpu().MilliValue())) + expectedCPUShares = int64(1 + ((expectedCPUShares-2)*9999)/262142) + // convert cgroup v1 cpu.shares value to cgroup v2 cpu.weight value + // https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2254-cgroup-v2#phase-1-convert-from-cgroups-v1-settings-to-v2 + var errs []error + err = e2epod.VerifyCgroupValue(f, pod, pod.Spec.Containers[0].Name, cpuWeightCgPath, strconv.FormatInt(expectedCPUShares, 10)) + if err != nil { + errs = append(errs, fmt.Errorf("failed to verify cpu request cgroup value: %w", err)) + } + + cpuLimCgPath := fmt.Sprintf("%s/%s", podCgPath, cgroupv2CPULimit) + cpuQuota := kubecm.MilliCPUToQuota(expectedResources.Limits.Cpu().MilliValue(), kubecm.QuotaPeriod) + expectedCPULimit := strconv.FormatInt(cpuQuota, 10) + expectedCPULimit = fmt.Sprintf("%s %s", expectedCPULimit, CPUPeriod) + err = e2epod.VerifyCgroupValue(f, pod, pod.Spec.Containers[0].Name, cpuLimCgPath, expectedCPULimit) + if err != nil { + errs = append(errs, fmt.Errorf("failed to verify cpu limit cgroup value: %w", err)) + } + + memLimCgPath := fmt.Sprintf("%s/%s", podCgPath, cgroupv2MemLimit) + expectedMemLim := strconv.FormatInt(expectedResources.Limits.Memory().Value(), 10) + err = e2epod.VerifyCgroupValue(f, pod, pod.Spec.Containers[0].Name, memLimCgPath, expectedMemLim) + if err != nil { + errs = append(errs, fmt.Errorf("failed to verify memory limit cgroup value: %w", err)) + } + return utilerrors.NewAggregate(errs) +} + +func podLevelResourcesTests(f *framework.Framework) { + type expectedPodConfig struct { + qos v1.PodQOSClass + // totalPodResources represents the aggregate resource requests + // and limits for the pod. If pod-level resource specifications + // are specified, totalPodResources is equal to pod-level resources. + // Otherwise, it is calculated by aggregating resource requests and + // limits from all containers within the pod.. + totalPodResources *resourceInfo + } + + type testCase struct { + name string + podResources *resourceInfo + containers []containerInfo + expected expectedPodConfig + } + + tests := []testCase{ + { + name: "Guaranteed QoS pod with container resources", + containers: []containerInfo{ + {Name: "c1", Resources: &resourceInfo{CPUReq: "50m", CPULim: "50m", MemReq: "70Mi", MemLim: "70Mi"}}, + {Name: "c2", Resources: &resourceInfo{CPUReq: "70m", CPULim: "70m", MemReq: "50Mi", MemLim: "50Mi"}}, + }, + expected: expectedPodConfig{ + qos: v1.PodQOSGuaranteed, + totalPodResources: &resourceInfo{CPUReq: "120m", CPULim: "120m", MemReq: "120Mi", MemLim: "120Mi"}, + }, + }, + { + name: "Guaranteed QoS pod, no container resources", + podResources: &resourceInfo{CPUReq: "100m", CPULim: "100m", MemReq: "100Mi", MemLim: "100Mi"}, + containers: []containerInfo{{Name: "c1"}, {Name: "c2"}}, + expected: expectedPodConfig{ + qos: v1.PodQOSGuaranteed, + totalPodResources: &resourceInfo{CPUReq: "100m", CPULim: "100m", MemReq: "100Mi", MemLim: "100Mi"}, + }, + }, + { + name: "Guaranteed QoS pod with container resources", + podResources: &resourceInfo{CPUReq: "100m", CPULim: "100m", MemReq: "100Mi", MemLim: "100Mi"}, + containers: []containerInfo{ + {Name: "c1", Resources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}}, + {Name: "c2", Resources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}}, + }, + expected: expectedPodConfig{ + qos: v1.PodQOSGuaranteed, + totalPodResources: &resourceInfo{CPUReq: "100m", CPULim: "100m", MemReq: "100Mi", MemLim: "100Mi"}, + }, + }, + { + name: "Guaranteed QoS pod, 1 container with resources", + podResources: &resourceInfo{CPUReq: "100m", CPULim: "100m", MemReq: "100Mi", MemLim: "100Mi"}, + containers: []containerInfo{ + {Name: "c1", Resources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}}, + {Name: "c2"}, + }, + expected: expectedPodConfig{ + qos: v1.PodQOSGuaranteed, + totalPodResources: &resourceInfo{CPUReq: "100m", CPULim: "100m", MemReq: "100Mi", MemLim: "100Mi"}, + }, + }, + { + name: "Burstable QoS pod, no container resources", + podResources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}, + containers: []containerInfo{ + {Name: "c1"}, + {Name: "c2"}, + }, + expected: expectedPodConfig{ + qos: v1.PodQOSBurstable, + totalPodResources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}, + }, + }, + { + name: "Burstable QoS pod with container resources", + podResources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}, + containers: []containerInfo{ + {Name: "c1", Resources: &resourceInfo{CPUReq: "20m", CPULim: "100m", MemReq: "20Mi", MemLim: "100Mi"}}, + {Name: "c2", Resources: &resourceInfo{CPUReq: "30m", CPULim: "100m", MemReq: "30Mi", MemLim: "100Mi"}}, + }, + expected: expectedPodConfig{ + qos: v1.PodQOSBurstable, + totalPodResources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}, + }, + }, + { + name: "Burstable QoS pod, 1 container with resources", + podResources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}, + containers: []containerInfo{ + {Name: "c1", Resources: &resourceInfo{CPUReq: "20m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}}, + {Name: "c2"}, + }, + expected: expectedPodConfig{ + qos: v1.PodQOSBurstable, + totalPodResources: &resourceInfo{CPUReq: "50m", CPULim: "100m", MemReq: "50Mi", MemLim: "100Mi"}, + }, + }, + } + + for _, tc := range tests { + ginkgo.It(tc.name, func(ctx context.Context) { + podMetadata := makeObjectMetadata("testpod", f.Namespace.Name) + testPod := makePod(&podMetadata, tc.podResources, tc.containers) + + ginkgo.By("creating pods") + podClient := e2epod.NewPodClient(f) + pod := podClient.CreateSync(ctx, testPod) + + ginkgo.By("verifying pod resources are as expected") + verifyPodResources(*pod, tc.podResources, tc.expected.totalPodResources) + + ginkgo.By("verifying pod QoS as expected") + verifyQoS(*pod, tc.expected.qos) + + ginkgo.By("verifying pod cgroup values") + err := verifyPodCgroups(ctx, f, pod, tc.expected.totalPodResources) + framework.ExpectNoError(err, "failed to verify pod's cgroup values: %v", err) + + ginkgo.By("verifying containers cgroup limits are same as pod container's cgroup limits") + err = verifyContainersCgroupLimits(f, pod) + framework.ExpectNoError(err, "failed to verify containers cgroup values: %v", err) + + ginkgo.By("deleting pods") + delErr := e2epod.DeletePodWithWait(ctx, f.ClientSet, pod) + framework.ExpectNoError(delErr, "failed to delete pod %s", delErr) + }) + } +} + +func verifyContainersCgroupLimits(f *framework.Framework, pod *v1.Pod) error { + var errs []error + for _, container := range pod.Spec.Containers { + if pod.Spec.Resources != nil && pod.Spec.Resources.Limits.Memory() != nil && + container.Resources.Limits.Memory() == nil { + expectedCgroupMemLimit := strconv.FormatInt(pod.Spec.Resources.Limits.Memory().Value(), 10) + err := e2epod.VerifyCgroupValue(f, pod, container.Name, fmt.Sprintf("%s/%s", cgroupFsPath, cgroupv2MemLimit), expectedCgroupMemLimit) + if err != nil { + errs = append(errs, fmt.Errorf("failed to verify memory limit cgroup value: %w", err)) + } + } + + if pod.Spec.Resources != nil && pod.Spec.Resources.Limits.Cpu() != nil && + container.Resources.Limits.Cpu() == nil { + cpuQuota := kubecm.MilliCPUToQuota(pod.Spec.Resources.Limits.Cpu().MilliValue(), kubecm.QuotaPeriod) + expectedCPULimit := strconv.FormatInt(cpuQuota, 10) + expectedCPULimit = fmt.Sprintf("%s %s", expectedCPULimit, CPUPeriod) + err := e2epod.VerifyCgroupValue(f, pod, container.Name, fmt.Sprintf("%s/%s", cgroupFsPath, cgroupv2CPULimit), expectedCPULimit) + if err != nil { + errs = append(errs, fmt.Errorf("failed to verify cpu limit cgroup value: %w", err)) + } + } + } + return utilerrors.NewAggregate(errs) +} diff --git a/test/e2e/feature/feature.go b/test/e2e/feature/feature.go index 415c27a4ec8..77a77ad7b91 100644 --- a/test/e2e/feature/feature.go +++ b/test/e2e/feature/feature.go @@ -277,6 +277,11 @@ var ( // TODO: document the feature (owning SIG, when to use this feature for a test) PodGarbageCollector = framework.WithFeature(framework.ValidFeatures.Add("PodGarbageCollector")) + // owner: sig-node + // Marks a test for for pod-level resources feature that requires + // PodLevelResources feature gate to be enabled. + PodLevelResources = framework.WithFeature(framework.ValidFeatures.Add("PodLevelResources")) + // TODO: document the feature (owning SIG, when to use this feature for a test) PodLifecycleSleepAction = framework.WithFeature(framework.ValidFeatures.Add("PodLifecycleSleepAction")) diff --git a/test/e2e/framework/pod/resize.go b/test/e2e/framework/pod/resize.go index 2e189d02ae7..bc01b346450 100644 --- a/test/e2e/framework/pod/resize.go +++ b/test/e2e/framework/pod/resize.go @@ -243,22 +243,10 @@ func VerifyPodStatusResources(gotPod *v1.Pod, wantCtrs []ResizableContainerInfo) return utilerrors.NewAggregate(errs) } -// isPodOnCgroupv2Node checks whether the pod is running on cgroupv2 node. -// TODO: Deduplicate this function with NPD cluster e2e test: -// https://github.com/kubernetes/kubernetes/blob/2049360379bcc5d6467769cef112e6e492d3d2f0/test/e2e/node/node_problem_detector.go#L369 -func isPodOnCgroupv2Node(f *framework.Framework, pod *v1.Pod) bool { - cmd := "mount -t cgroup2" - out, _, err := ExecCommandInContainerWithFullOutput(f, pod.Name, pod.Spec.Containers[0].Name, "/bin/sh", "-c", cmd) - if err != nil { - return false - } - return len(out) != 0 -} - func VerifyPodContainersCgroupValues(ctx context.Context, f *framework.Framework, pod *v1.Pod, tcInfo []ResizableContainerInfo) error { ginkgo.GinkgoHelper() if podOnCgroupv2Node == nil { - value := isPodOnCgroupv2Node(f, pod) + value := IsPodOnCgroupv2Node(f, pod) podOnCgroupv2Node = &value } cgroupMemLimit := Cgroupv2MemLimit @@ -269,21 +257,7 @@ func VerifyPodContainersCgroupValues(ctx context.Context, f *framework.Framework cgroupCPULimit = CgroupCPUQuota cgroupCPURequest = CgroupCPUShares } - verifyCgroupValue := func(cName, cgPath, expectedCgValue string) error { - cmd := fmt.Sprintf("head -n 1 %s", cgPath) - framework.Logf("Namespace %s Pod %s Container %s - looking for cgroup value %s in path %s", - pod.Namespace, pod.Name, cName, expectedCgValue, cgPath) - cgValue, _, err := ExecCommandInContainerWithFullOutput(f, pod.Name, cName, "/bin/sh", "-c", cmd) - if err != nil { - return fmt.Errorf("failed to read cgroup %q for container %s: %w", cgPath, cName, err) - } - cgValue = strings.Trim(cgValue, "\n") - if cgValue != expectedCgValue { - return fmt.Errorf("container %s cgroup %q doesn't match expected: got %q want %q", - cName, cgPath, cgValue, expectedCgValue) - } - return nil - } + var errs []error for _, ci := range tcInfo { if ci.Resources == nil { @@ -320,10 +294,10 @@ func VerifyPodContainersCgroupValues(ctx context.Context, f *framework.Framework expectedCPUShares = int64(1 + ((expectedCPUShares-2)*9999)/262142) } if expectedMemLimitString != "0" { - errs = append(errs, verifyCgroupValue(ci.Name, cgroupMemLimit, expectedMemLimitString)) + errs = append(errs, VerifyCgroupValue(f, pod, ci.Name, cgroupMemLimit, expectedMemLimitString)) } - errs = append(errs, verifyCgroupValue(ci.Name, cgroupCPULimit, expectedCPULimitString)) - errs = append(errs, verifyCgroupValue(ci.Name, cgroupCPURequest, strconv.FormatInt(expectedCPUShares, 10))) + errs = append(errs, VerifyCgroupValue(f, pod, ci.Name, cgroupCPULimit, expectedCPULimitString)) + errs = append(errs, VerifyCgroupValue(f, pod, ci.Name, cgroupCPURequest, strconv.FormatInt(expectedCPUShares, 10))) } } return utilerrors.NewAggregate(errs) diff --git a/test/e2e/framework/pod/utils.go b/test/e2e/framework/pod/utils.go index cf33d9f1771..d28a26c3311 100644 --- a/test/e2e/framework/pod/utils.go +++ b/test/e2e/framework/pod/utils.go @@ -19,11 +19,13 @@ package pod import ( "flag" "fmt" + "strings" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" + "k8s.io/kubernetes/test/e2e/framework" imageutils "k8s.io/kubernetes/test/utils/image" psaapi "k8s.io/pod-security-admission/api" psapolicy "k8s.io/pod-security-admission/policy" @@ -275,3 +277,33 @@ func FindContainerStatusInPod(pod *v1.Pod, containerName string) *v1.ContainerSt } return nil } + +// VerifyCgroupValue verifies that the given cgroup path has the expected value in +// the specified container of the pod. It execs into the container to retrive the +// cgroup value and compares it against the expected value. +func VerifyCgroupValue(f *framework.Framework, pod *v1.Pod, cName, cgPath, expectedCgValue string) error { + cmd := fmt.Sprintf("head -n 1 %s", cgPath) + framework.Logf("Namespace %s Pod %s Container %s - looking for cgroup value %s in path %s", + pod.Namespace, pod.Name, cName, expectedCgValue, cgPath) + cgValue, _, err := ExecCommandInContainerWithFullOutput(f, pod.Name, cName, "/bin/sh", "-c", cmd) + if err != nil { + return fmt.Errorf("failed to find expected value %q in container cgroup %q", expectedCgValue, cgPath) + } + cgValue = strings.Trim(cgValue, "\n") + if cgValue != expectedCgValue { + return fmt.Errorf("cgroup value %q not equal to expected %q", cgValue, expectedCgValue) + } + return nil +} + +// IsPodOnCgroupv2Node checks whether the pod is running on cgroupv2 node. +// TODO: Deduplicate this function with NPD cluster e2e test: +// https://github.com/kubernetes/kubernetes/blob/2049360379bcc5d6467769cef112e6e492d3d2f0/test/e2e/node/node_problem_detector.go#L369 +func IsPodOnCgroupv2Node(f *framework.Framework, pod *v1.Pod) bool { + cmd := "mount -t cgroup2" + out, _, err := ExecCommandInContainerWithFullOutput(f, pod.Name, pod.Spec.Containers[0].Name, "/bin/sh", "-c", cmd) + if err != nil { + return false + } + return len(out) != 0 +} diff --git a/test/featuregates_linter/test_data/versioned_feature_list.yaml b/test/featuregates_linter/test_data/versioned_feature_list.yaml index e69d01200ab..50e80cd6782 100644 --- a/test/featuregates_linter/test_data/versioned_feature_list.yaml +++ b/test/featuregates_linter/test_data/versioned_feature_list.yaml @@ -918,6 +918,12 @@ lockToDefault: true preRelease: GA version: "1.32" +- name: PodLevelResources + versionedSpecs: + - default: false + lockToDefault: false + preRelease: Alpha + version: "1.32" - name: PodLifecycleSleepAction versionedSpecs: - default: false diff --git a/test/utils/node.go b/test/utils/node.go index 9388251acd0..3483088aa72 100644 --- a/test/utils/node.go +++ b/test/utils/node.go @@ -16,7 +16,7 @@ limitations under the License. package utils -import "k8s.io/api/core/v1" +import v1 "k8s.io/api/core/v1" // GetNodeCondition extracts the provided condition from the given status and returns that. // Returns nil and -1 if the condition is not present, and the index of the located condition.