From d7f488b5e341dec8f6ef180f488e788a4103821c Mon Sep 17 00:00:00 2001 From: ndixita Date: Wed, 16 Oct 2024 17:01:47 +0000 Subject: [PATCH 01/15] API changes for Pod Level Resources 1. Add Resources struct to PodSpec struct in both external and internal API packages 2. Adding feature gate and logic for dropping disabled fields for Pod Level Resources KEP: enhancements/keps/sig-node/2837-pod-level-resource-spec --- pkg/api/pod/util.go | 31 +++++ pkg/api/pod/util_test.go | 143 ++++++++++++++++++++++++ pkg/apis/core/types.go | 14 +++ pkg/features/kube_features.go | 7 ++ pkg/features/versioned_kube_features.go | 4 + staging/src/k8s.io/api/core/v1/types.go | 14 +++ 6 files changed, 213 insertions(+) diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index 9ad259147d0fa..b472d00fe106b 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -621,6 +621,7 @@ func dropDisabledFields( } } + dropDisabledPodLevelResources(podSpec, oldPodSpec) dropDisabledProcMountField(podSpec, oldPodSpec) dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec) @@ -674,6 +675,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 +1059,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 67ab805b7a689..2f2f58e3f81ec 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/core/types.go b/pkg/apis/core/types.go index a040527765c4e..220d018912f1c 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/features/kube_features.go b/pkg/features/kube_features.go index fe387e8f6a990..7a6027aa1ac68 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -835,6 +835,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 693067e6c3cf7..7dc036b87d859 100644 --- a/pkg/features/versioned_kube_features.go +++ b/pkg/features/versioned_kube_features.go @@ -567,6 +567,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/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index d1700f10e735b..69e1428d4c9a2 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -4080,6 +4080,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 From 85488b5f10bc6128dab69e4bdd94cec2c005c7f5 Mon Sep 17 00:00:00 2001 From: ndixita Date: Wed, 16 Oct 2024 20:34:52 +0000 Subject: [PATCH 02/15] Generated files and compatability data from API changes --- api/openapi-spec/swagger.json | 4 + api/openapi-spec/v3/api__v1_openapi.json | 8 + .../v3/apis__apps__v1_openapi.json | 8 + .../v3/apis__batch__v1_openapi.json | 8 + pkg/apis/apps/v1/zz_generated.defaults.go | 16 + .../apps/v1beta1/zz_generated.defaults.go | 8 + .../apps/v1beta2/zz_generated.defaults.go | 16 + pkg/apis/batch/v1/zz_generated.defaults.go | 8 + .../batch/v1beta1/zz_generated.defaults.go | 4 + pkg/apis/core/v1/zz_generated.conversion.go | 2 + pkg/apis/core/v1/zz_generated.defaults.go | 12 + pkg/apis/core/zz_generated.deepcopy.go | 5 + .../v1beta1/zz_generated.defaults.go | 12 + pkg/generated/openapi/zz_generated.openapi.go | 8 +- .../src/k8s.io/api/core/v1/generated.pb.go | 2072 +++++++++-------- .../src/k8s.io/api/core/v1/generated.proto | 15 + .../core/v1/types_swagger_doc_generated.go | 1 + .../api/core/v1/zz_generated.deepcopy.go | 5 + .../api/testdata/HEAD/apps.v1.DaemonSet.json | 16 +- .../api/testdata/HEAD/apps.v1.DaemonSet.pb | Bin 10938 -> 11006 bytes .../api/testdata/HEAD/apps.v1.DaemonSet.yaml | 8 + .../api/testdata/HEAD/apps.v1.Deployment.json | 16 +- .../api/testdata/HEAD/apps.v1.Deployment.pb | Bin 10951 -> 11019 bytes .../api/testdata/HEAD/apps.v1.Deployment.yaml | 8 + .../api/testdata/HEAD/apps.v1.ReplicaSet.json | 16 +- .../api/testdata/HEAD/apps.v1.ReplicaSet.pb | Bin 10868 -> 10936 bytes .../api/testdata/HEAD/apps.v1.ReplicaSet.yaml | 8 + .../testdata/HEAD/apps.v1.StatefulSet.json | 16 +- .../api/testdata/HEAD/apps.v1.StatefulSet.pb | Bin 12039 -> 12107 bytes .../testdata/HEAD/apps.v1.StatefulSet.yaml | 8 + .../HEAD/apps.v1beta1.Deployment.json | 16 +- .../testdata/HEAD/apps.v1beta1.Deployment.pb | Bin 10960 -> 11028 bytes .../HEAD/apps.v1beta1.Deployment.yaml | 8 + .../HEAD/apps.v1beta1.StatefulSet.json | 16 +- .../testdata/HEAD/apps.v1beta1.StatefulSet.pb | Bin 12044 -> 12112 bytes .../HEAD/apps.v1beta1.StatefulSet.yaml | 8 + .../testdata/HEAD/apps.v1beta2.DaemonSet.json | 16 +- .../testdata/HEAD/apps.v1beta2.DaemonSet.pb | Bin 10943 -> 11011 bytes .../testdata/HEAD/apps.v1beta2.DaemonSet.yaml | 8 + .../HEAD/apps.v1beta2.Deployment.json | 16 +- .../testdata/HEAD/apps.v1beta2.Deployment.pb | Bin 10956 -> 11024 bytes .../HEAD/apps.v1beta2.Deployment.yaml | 8 + .../HEAD/apps.v1beta2.ReplicaSet.json | 16 +- .../testdata/HEAD/apps.v1beta2.ReplicaSet.pb | Bin 10873 -> 10941 bytes .../HEAD/apps.v1beta2.ReplicaSet.yaml | 8 + .../HEAD/apps.v1beta2.StatefulSet.json | 16 +- .../testdata/HEAD/apps.v1beta2.StatefulSet.pb | Bin 12044 -> 12112 bytes .../HEAD/apps.v1beta2.StatefulSet.yaml | 8 + .../api/testdata/HEAD/batch.v1.CronJob.json | 16 +- .../api/testdata/HEAD/batch.v1.CronJob.pb | Bin 11531 -> 11599 bytes .../api/testdata/HEAD/batch.v1.CronJob.yaml | 8 + .../api/testdata/HEAD/batch.v1.Job.json | 16 +- .../k8s.io/api/testdata/HEAD/batch.v1.Job.pb | Bin 11157 -> 11225 bytes .../api/testdata/HEAD/batch.v1.Job.yaml | 8 + .../testdata/HEAD/batch.v1beta1.CronJob.json | 16 +- .../testdata/HEAD/batch.v1beta1.CronJob.pb | Bin 11536 -> 11604 bytes .../testdata/HEAD/batch.v1beta1.CronJob.yaml | 8 + .../k8s.io/api/testdata/HEAD/core.v1.Pod.json | 16 +- .../k8s.io/api/testdata/HEAD/core.v1.Pod.pb | Bin 12097 -> 12165 bytes .../k8s.io/api/testdata/HEAD/core.v1.Pod.yaml | 8 + .../testdata/HEAD/core.v1.PodTemplate.json | 16 +- .../api/testdata/HEAD/core.v1.PodTemplate.pb | Bin 10704 -> 10772 bytes .../testdata/HEAD/core.v1.PodTemplate.yaml | 8 + .../HEAD/core.v1.ReplicationController.json | 16 +- .../HEAD/core.v1.ReplicationController.pb | Bin 10826 -> 10894 bytes .../HEAD/core.v1.ReplicationController.yaml | 8 + .../HEAD/extensions.v1beta1.DaemonSet.json | 16 +- .../HEAD/extensions.v1beta1.DaemonSet.pb | Bin 10951 -> 11019 bytes .../HEAD/extensions.v1beta1.DaemonSet.yaml | 8 + .../HEAD/extensions.v1beta1.Deployment.json | 16 +- .../HEAD/extensions.v1beta1.Deployment.pb | Bin 10966 -> 11034 bytes .../HEAD/extensions.v1beta1.Deployment.yaml | 8 + .../HEAD/extensions.v1beta1.ReplicaSet.json | 16 +- .../HEAD/extensions.v1beta1.ReplicaSet.pb | Bin 10879 -> 10947 bytes .../HEAD/extensions.v1beta1.ReplicaSet.yaml | 8 + .../applyconfigurations/core/v1/podspec.go | 9 + .../applyconfigurations/internal/internal.go | 3 + 77 files changed, 1652 insertions(+), 1028 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 4ee9959f119eb..63874713deec1 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -9750,6 +9750,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 9cda79f9364e1..fa6051f0bcb70 100644 --- a/api/openapi-spec/v3/api__v1_openapi.json +++ b/api/openapi-spec/v3/api__v1_openapi.json @@ -5670,6 +5670,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 3f5e84fcd495c..ca2488e32d467 100644 --- a/api/openapi-spec/v3/apis__apps__v1_openapi.json +++ b/api/openapi-spec/v3/apis__apps__v1_openapi.json @@ -3924,6 +3924,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 21ff8dc1c1ec9..0db1d545d5382 100644 --- a/api/openapi-spec/v3/apis__batch__v1_openapi.json +++ b/api/openapi-spec/v3/apis__batch__v1_openapi.json @@ -3128,6 +3128,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/apis/apps/v1/zz_generated.defaults.go b/pkg/apis/apps/v1/zz_generated.defaults.go index ef249608d9043..99bcf2c7ee5dd 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 648ba507bc491..3b54c802a345c 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 a1b414ce9b64f..35aacff862423 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 70a1b5fef7c4e..eb6ce80a1f9cb 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 ba79028e71060..7e517afd0c170 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/v1/zz_generated.conversion.go b/pkg/apis/core/v1/zz_generated.conversion.go index 9c82744209842..6a842771a111b 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 44d332981c053..3b6eb4f0a93fc 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/zz_generated.deepcopy.go b/pkg/apis/core/zz_generated.deepcopy.go index ef70c1fb32f5d..a9d32c1ca69cb 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 122455427fa47..bfe82d2deef2f 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/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 66ea21a494595..3e77c2662fa98 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -28584,12 +28584,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/staging/src/k8s.io/api/core/v1/generated.pb.go b/staging/src/k8s.io/api/core/v1/generated.pb.go index 84fb2c61860a5..9d466c6d79dcc 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 40e0fb09f197c..d00e0f93a4c38 100644 --- a/staging/src/k8s.io/api/core/v1/generated.proto +++ b/staging/src/k8s.io/api/core/v1/generated.proto @@ -4455,6 +4455,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_swagger_doc_generated.go b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go index 34adb27962b58..eda45be2f0430 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 @@ -1830,6 +1830,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 1d65a0d7e2194..3f669092ef9ef 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 a6c89e2e507a1..433aa5674064c 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 b75325807c5aa8e4a988d158ac8aed3c983c8088..b9aaa8c0cfdd7a1065887ff5a75d57be4648911b 100644 GIT binary patch delta 47 zcmV+~0MP%sRsL0w8v)(19AyCmq*4l#pb1R_5|B}`9b^Fls!p>y1Lh_I!UB_i4nmV4 FDw>}T5NH4Z delta 40 ycmV+@0N4NiRk~G>8v&WI9AyCm;86;bpb1R_63kGs9b^Fl=1sFY1Lh`^WGb3DpAPZ> 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 b2fe097d004bd..cf27e24793bd0 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 f44ffb4bb930f..6e68ee719130f 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 ddaa4b71e0835536d75e6c25f8b3ce1e0ad63ef1..e51e1c32335c3c582dd1221d425872b34c7e25f2 100644 GIT binary patch delta 49 zcmV-10M7r%Rf|@T90BRE9c2Lps!|965|beqO#>Q`QL!Fm0RpN{vpoamCIZ3&lYtIG HlOrmml6Mc* delta 42 ycmeAUJ03bghH>de*;Gc>XMr4yLX#CGe3_-r1ZuFs<_2Y|H#ijp-26uFs<_2Y{&dejrkCh<77o1^~tOF Lq&BN)J!Sy_8XOW# delta 52 zcmX>d*B&=Pmg!0CM)?#*)|My^Mxn_Y#eJEj&IEu&n4bA=wqt&##;kOg9mL;!Ub}`F E0OCdw=Kufz 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 7b747e00f9094..ce0f847f4c9ab 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 9cedd55ad0a3b..715007f246f94 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 c8b30c7b194a8491c0f0f987a8db26714757afed..4ea65f9027edb28f72d7edb2982bf83057cb396f 100644 GIT binary patch delta 52 zcmcZ*Iwfp^BGcQ@&37167+Kc^aWD!^J}BnPEHxnzEX1_RZ?hBgGc~3|Op_HzCuXI) M>|pWD6SQlX0jx(A6aWAK delta 46 zcmcZ**Aq8EiRnq~<^ru0M%I=n4o0ELaXh}vQfC6dLQKzmH#;*wQ`?-Y^_T?!f(H;@ 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 d85954d2f3d7d..aa3096aeec20e 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 bf085d4e2039d..4e74dd3716dbe 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 6a0fd96123f03c8d9736571afc19bf7f69c6fa11..f7f0a63e26c58db78ad1ebb19d7650a26ff52917 100644 GIT binary patch delta 46 zcmV+}0MY-yRfATLAOYR6A!Pvqq*9Zy7f%9^QM1_rWB~%IPP0D)<|YEd0+SsiLX#yb EnwRttegFUf delta 39 xcmV+?0NDS7R=-t{AOV@NA!Pvq;8ByY7f%ArP_x+qWB~%^O|w4(<|dPADw;8B4^sdD 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 fc59afcd925ea..040450a2f10f8 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 2081c9ac26daf..4b0e0e3e50278 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 5a95130c796bfdf0c662ace90665fc7e29756cf8..f46ae2c4c8d348ae398e4adc06e5a035fb5fe90e 100644 GIT binary patch delta 52 zcmX>TIw5R=BGb#z&37167+F^ZaWD!^J}BnPEHxnzEX1_RZ?hBgGc~3|Op_HsBGa_c&37167+IeNaxe-_J}BnPEHxnzEX1_RZ?hBgGc~3|Op_HzCuXI) M>|pWD6SQlX0jx(A6aWAK delta 46 zcmcZ**Aq8EiRnq~<^ru0M%I=n4o0ELaXh}vQfC6dLQKzmH#;*wQ`?-Y^_T?!f(H;@ 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 2eff78ed0cdb1..9f6105ed55b80 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 a562c92d1fe81..cb46e57e4455c 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 012d2325a9971707b0b2ddf73b77c73d61d52138..ff6c0f89782c105f1df0236b03c2ffa2b649e017 100644 GIT binary patch delta 72 zcmeBf)g3iKis^Oa<_(M~jErrQ8yR((j)sDH9QT7b7zLOlm?yvI^<&jwG&&Oi7H4|q PyZI{1GquhCwF4Ld^?nxu 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 7b5008f31ff26..773dce02c6676 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 661fbd423b808..ce6f5c104110d 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 9f1a340844b6c021517e790945399111e83d1c8e..0ea948f1f7279f298a223268ec27d29380539836 100644 GIT binary patch delta 47 zcmbOlelvW67}MVHjS?x0OpAjj*D;1OO$Y?@R{3qVWqzi{bcktkBcIb|WvwDc0GQ?w A4*&oF delta 40 tcmcZ^J~e!T7}L+NjS?x0O!tE(*D;1Ooe2Q*p80OJWqzi%IbExW5dcJg4;26a 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 62f868d0c13a3..b5c1219e2b08a 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 ec951ff67bb64..4b1b0704bbb9d 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 2e9cc214316b1f7ce542187ac6cf771986633782..abe891dbe0f49fcb9a3a03468fe93b5c438e99b0 100644 GIT binary patch delta 60 zcmbObbtP(oJk$E9jfyFZjHf2IG3qjPhJiRti-RXiGlw%x2n6$1`E9<%@=T5C5YuEi L0jJFev;!CchBXwO delta 53 zcmcZ-H6dz(Jk#sQjfyFZjBS(K7|n+`EI_&@=R?ri%tL| E01@>P`~Uy| 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 542bc2ead149e..2a6e4e78b6e74 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 12b91a85fcf67..aedd4a22dc09d 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 7be883eeac6f5f96adc04c675c01caa4d05e73f1..9b44310ba347446bc23a00f6b2152a9fb45c0de6 100644 GIT binary patch delta 46 zcmX>Y*BURFZBfj?#mU7~W+=oQke?#-exqP2BhxCs%@vH#)R+!2P4?Gw+8n3xm<<3% C+YY_} delta 39 vcmZpTKNu&NZBfj?#mU7~W+=oQke?#7dZS<}BhxeA%@vH#)HW~Dc+3U>=+q41 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 a76207e05eb73..31532e145a2f8 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 072f216ed860c..7ab8ec402b795 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 3cc9b29777c97bab17e1e97efe958dab084af04f..8efa534315afd2cc9a8b1d346d671bd18db9061b 100644 GIT binary patch delta 42 ucmcZ*JSAj;1k<(A@nN0&Ci&gsWBa5nykg=#3IF@!~g(S5Dmxx delta 35 ncmbOdav^wv1kV(ib{GiRoF$=CzC|j7(btCwDO_GEE4Ca98e+8s7Qjp=^q=KG8(jLge|xF#p?_%aJk2m}i-t@7LK%KS`?=@8T8jnYn&wYBB| E0NRKW4FCWD delta 43 wcmeAUJ03bgjcIo1=KG8(jLeS$xh5y@_%aKf2>=T)J@eh{%KS`ia=z9a0BEWZR{#J2 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 894d06f21399e..ca7ee89373608 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 fc26c1a9e6173..5a6cbb46e2376 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 ecd47966df0032742713243a3f20eaea9b8470d1..480fc47fdb99f0110d194a123847dafe785961e1 100644 GIT binary patch delta 48 zcmcZ>IxB2~I@8c`afiXI@6_)%^SH=7#SB%?qdvMIuiiqF+KC$?7{p@ZE}%TAOKd+599y< 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 43b01319e2394..75188bb3f4a5c 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 8134e044f190a..96f6eb94b1c06 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 f14cc8354fa0b..cf69098f6eb17 100644 --- a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go +++ b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go @@ -7169,6 +7169,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 From 502e0f55c43291af800e8633760d461343bd39b3 Mon Sep 17 00:00:00 2001 From: ndixita Date: Thu, 24 Oct 2024 21:13:54 +0000 Subject: [PATCH 03/15] Adding support for pod level resources in kubectl 1. Add support for pod level resources in kubectl 2. Reuse the existing method to describe container resources and generalize it to describe both pod and container level resources --- .../k8s.io/kubectl/pkg/describe/describe.go | 22 +++++-- .../kubectl/pkg/describe/describe_test.go | 65 +++++++++++++++++++ 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index 1e6acd75e13d2..75b1662e074c9 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 27930c0846e73..946ece04b68c7 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 { From a2ddde877c5b0a5bab25c4fefaca02175eac73b7 Mon Sep 17 00:00:00 2001 From: ndixita Date: Sat, 26 Oct 2024 01:19:52 +0000 Subject: [PATCH 04/15] Adding the logic to set default pod-level request as following: 1. If pod-level limit is set, pod-level request is unset and container-level request is set: derive pod-level request from container-level requests 2. If pod-level limit is set, pod-level request is unset and container-level request is unset: set pod-level request equal to pod-level limit --- pkg/apis/core/v1/defaults.go | 61 ++ pkg/apis/core/v1/defaults_test.go | 874 ++++++++++++++++++ pkg/apis/core/validation/validation_test.go | 1 + .../component-helpers/resource/helpers.go | 141 ++- .../resource/helpers_test.go | 519 +++++++++++ 5 files changed, 1574 insertions(+), 22 deletions(-) diff --git a/pkg/apis/core/v1/defaults.go b/pkg/apis/core/v1/defaults.go index aa5f448f791dc..a058c5f7c7fb1 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 d8292d852626d..a460a274b077e 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/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index e22078723a400..23d500e48fe85 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -20656,6 +20656,7 @@ func TestValidateOSFields(t *testing.T) { "ResourceClaims[*].Name", "ResourceClaims[*].ResourceClaimName", "ResourceClaims[*].ResourceClaimTemplateName", + "Resources", "RestartPolicy", "RuntimeClassName", "SchedulerName", diff --git a/staging/src/k8s.io/component-helpers/resource/helpers.go b/staging/src/k8s.io/component-helpers/resource/helpers.go index a8b252cfdd4f6..3bdcef6e8169d 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 f2d984cf65dec..e146a311971a8 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), From 8a8dc27b4e1317ff58c776643d7f175c870d4dbe Mon Sep 17 00:00:00 2001 From: ndixita Date: Sun, 27 Oct 2024 22:26:06 +0000 Subject: [PATCH 05/15] Adding the logic to validate pod-level resources as following: 1. The effective container requests cannot be greater than pod-level requests 2. Inidividual container limits cannot be greater than pod-level limits 3. Only CPU & Memory are supported at pod-level 4. Inplace container resources updates are not supported if pod-level resources are set Note: effective container requests cannot be greater than pod-level limits is supported by transitivity. Effective container requests <= pod-level requests && pod-level requests <= pod-level limits; Therefore effective container requests <= pod-level limits Signed-off-by: ndixita --- pkg/api/pod/testing/make.go | 6 + pkg/api/pod/util.go | 1 + pkg/apis/core/validation/validation.go | 124 ++++- pkg/apis/core/validation/validation_test.go | 534 +++++++++++++++++++- pkg/apis/node/validation/validation.go | 2 +- 5 files changed, 651 insertions(+), 16 deletions(-) diff --git a/pkg/api/pod/testing/make.go b/pkg/api/pod/testing/make.go index b73de5d1a11b3..71c29614a7038 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 b472d00fe106b..152a4ebe37fe8 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, diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 9e2868f683e02..da98ab06f0daa 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")} @@ -6403,6 +6492,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 { @@ -6750,8 +6855,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") @@ -6764,7 +6877,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)...) @@ -6783,7 +6896,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 23d500e48fe85..b1890997e186f 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 @@ -23083,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{ @@ -23099,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{ @@ -23110,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{ @@ -23121,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) } }) @@ -23136,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{ @@ -23147,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") } }) @@ -25113,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/node/validation/validation.go b/pkg/apis/node/validation/validation.go index 4e0a20aaf1e54..d66e82b93825f 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{}) } From 6db40446de76dc90497d000521802bcf9645a214 Mon Sep 17 00:00:00 2001 From: ndixita Date: Tue, 29 Oct 2024 08:10:06 +0000 Subject: [PATCH 06/15] Scheduler changes: 1. Use pod-level resource when feature is enabled and resources are set at pod-level 2. Edge case handling: When a pod defines only CPU or memory limits at pod-level (but not both), and container-level requests/limits are unset, the pod-level requests stay empty for the resource without a pod-limit. The container's request for that resource is then set to the default request value from schedutil. --- pkg/scheduler/eventhandlers.go | 4 +- .../framework/plugins/feature/feature.go | 1 + .../framework/plugins/noderesources/fit.go | 33 ++- .../plugins/noderesources/fit_test.go | 101 ++++++- .../noderesources/resource_allocation.go | 3 + pkg/scheduler/framework/plugins/registry.go | 1 + pkg/scheduler/framework/types.go | 73 ++++- pkg/scheduler/framework/types_test.go | 261 +++++++++++++++++- pkg/scheduler/testing/wrappers.go | 6 + 9 files changed, 451 insertions(+), 32 deletions(-) diff --git a/pkg/scheduler/eventhandlers.go b/pkg/scheduler/eventhandlers.go index b3212174cbec7..3b009d76fb613 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 ff1920cca57c6..d4c5cc62d607f 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 0a7f387a42d8d..cd464dd512141 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 e1181288704c3..d118e6f2503b2 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 118fb7e07c1f0..e8526629485ab 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 009b6c89967fb..ae50798a3d67f 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 4e63acf95c4d0..ac671899771eb 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 8972aca1cdb8a..2120c840fc39c 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 a38b272100be9..96970b8448624 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{ From 26f11c458620751733250b35d1f60c9ed2a96e57 Mon Sep 17 00:00:00 2001 From: ndixita Date: Wed, 30 Oct 2024 01:24:36 +0000 Subject: [PATCH 07/15] QOS changes for Pod Level resources --- pkg/apis/core/helper/qos/qos.go | 109 +++++++++++------- pkg/apis/core/v1/helper/qos/qos.go | 108 +++++++++++------ pkg/apis/core/v1/helper/qos/qos_test.go | 90 ++++++++++++++- .../src/k8s.io/kubectl/pkg/util/qos/qos.go | 103 +++++++++++------ 4 files changed, 291 insertions(+), 119 deletions(-) diff --git a/pkg/apis/core/helper/qos/qos.go b/pkg/apis/core/helper/qos/qos.go index b32fffa0e3fa3..2b0cdcfff6eb9 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,44 @@ 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 - } - } + // 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) } - // 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 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 } } - - if !qosLimitsFound.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 } - // 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/v1/helper/qos/qos.go b/pkg/apis/core/v1/helper/qos/qos.go index 79e2eb2abd2ef..66e512b16d080 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,89 @@ 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 - } - } + // 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) } - // 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 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 } } - - if !qosLimitsFound.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 + processResourceList(requests, container.Resources.Requests) + // process limits + processResourceList(limits, container.Resources.Limits) + qosLimitResources := getQOSResources(container.Resources.Limits) + if !qosLimitResources.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 d16c17a14e737..29b9302c9cf9c 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/staging/src/k8s.io/kubectl/pkg/util/qos/qos.go b/staging/src/k8s.io/kubectl/pkg/util/qos/qos.go index 68b1b9072a9d4..1b102a0fe56a1 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 - } - } + if pod.Spec.Resources != nil { + if pod.Spec.Resources.Requests != nil { + // process requests + processResourceList(requests, pod.Spec.Resources.Requests) } - // 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.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 } } - - if !qosLimitsFound.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 } From 5ea57fb3b45d836034bd972e2366b21f0452b061 Mon Sep 17 00:00:00 2001 From: ndixita Date: Wed, 30 Oct 2024 05:53:36 +0000 Subject: [PATCH 08/15] cgroup configuration changes: 1. Pod cgrooup configured to use resources from pod spec if feature is enabled and resources are set at pod-level 2. Container cgroup limits defaulted to pod-level limits is container limits are not set --- pkg/kubelet/cm/helpers_linux.go | 20 ++- pkg/kubelet/cm/helpers_linux_test.go | 129 +++++++++++++++++- pkg/kubelet/cm/qos_container_manager_linux.go | 6 +- .../kuberuntime_container_linux.go | 34 ++++- .../kuberuntime_container_linux_test.go | 39 +++++- .../kuberuntime/kuberuntime_sandbox_linux.go | 4 + 6 files changed, 214 insertions(+), 18 deletions(-) diff --git a/pkg/kubelet/cm/helpers_linux.go b/pkg/kubelet/cm/helpers_linux.go index 5500621c9abb1..9ea190aba71c1 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 1399a2acf0ca4..92bab2549759a 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 441aa952b6559..0f88e10ff69bc 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 61dba0e736ca3..34856be8c7315 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" @@ -95,6 +96,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 @@ -102,7 +131,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 b9425b61efacc..69960b72d288e 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 8415103737a9e..6345e3af0a086 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) From 5a64597d2e4240d7998338150b17a2be1793e572 Mon Sep 17 00:00:00 2001 From: ndixita Date: Thu, 31 Oct 2024 21:46:53 +0000 Subject: [PATCH 09/15] Adding OOM Score adjustment formula changes that takes pod level resources into account Signed-off-by: ndixita --- pkg/kubelet/qos/helpers.go | 28 +++ pkg/kubelet/qos/policy.go | 32 ++- pkg/kubelet/qos/policy_test.go | 434 ++++++++++++++++++++++++++++++++- 3 files changed, 490 insertions(+), 4 deletions(-) diff --git a/pkg/kubelet/qos/helpers.go b/pkg/kubelet/qos/helpers.go index 2b327e5a7d3d0..17ed12e3b3385 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 c4beb95d41038..f12da0e5bfb2b 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 c09cb1e6891cc..a7a7980f3f94d 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 { From 99a6153a4f39d0248e21fad5c1007d2ce9e4ee6c Mon Sep 17 00:00:00 2001 From: ndixita Date: Sun, 3 Nov 2024 22:01:48 +0000 Subject: [PATCH 10/15] e2e tests Signed-off-by: ndixita --- test/e2e/common/node/pod_level_resources.go | 408 ++++++++++++++++++++ test/e2e/feature/feature.go | 5 + test/e2e/framework/pod/resize.go | 36 +- test/e2e/framework/pod/utils.go | 32 ++ test/utils/node.go | 2 +- 5 files changed, 451 insertions(+), 32 deletions(-) create mode 100644 test/e2e/common/node/pod_level_resources.go 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 0000000000000..8dbe18b1d8b75 --- /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 d703a15da48f1..d58ab30da3f1f 100644 --- a/test/e2e/feature/feature.go +++ b/test/e2e/feature/feature.go @@ -268,6 +268,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 2e189d02ae785..bc01b3464500b 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 cf33d9f1771f0..d28a26c33119b 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/utils/node.go b/test/utils/node.go index 9388251acd096..3483088aa7224 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. From 28dea49c044052063135411b7334db1c1a0eed07 Mon Sep 17 00:00:00 2001 From: ndixita Date: Mon, 4 Nov 2024 21:40:47 +0000 Subject: [PATCH 11/15] Limit Range changes to validate against Pod Level Resources --- plugin/pkg/admission/limitranger/admission.go | 65 ++++++++++++- .../admission/limitranger/admission_test.go | 91 +++++++++++++++---- 2 files changed, 134 insertions(+), 22 deletions(-) diff --git a/plugin/pkg/admission/limitranger/admission.go b/plugin/pkg/admission/limitranger/admission.go index db02fbd7dd4d9..55a65b056bb0d 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 d8c4a2d3d1e43..17fc3211af358 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) + } + }) } } From 777221421a007810992a73ac6670c0062fe9ddc8 Mon Sep 17 00:00:00 2001 From: ndixita Date: Mon, 4 Nov 2024 23:49:13 +0000 Subject: [PATCH 12/15] Resource Quota enforcement changes for Pod Level Resources --- pkg/quota/v1/evaluator/core/pods.go | 14 ++++ pkg/quota/v1/evaluator/core/pods_test.go | 97 ++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/pkg/quota/v1/evaluator/core/pods.go b/pkg/quota/v1/evaluator/core/pods.go index ac438685db008..8efdec6cc2b23 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 11de6569212b0..15f9945f29c1f 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) From 9813d85071442c2fdd2533d5cab07f3bf89f907b Mon Sep 17 00:00:00 2001 From: ndixita Date: Wed, 6 Nov 2024 04:14:49 +0000 Subject: [PATCH 13/15] Fixing linter failures --- .../test_data/versioned_feature_list.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/featuregates_linter/test_data/versioned_feature_list.yaml b/test/featuregates_linter/test_data/versioned_feature_list.yaml index 774a7a5f849d1..725c63d4451f8 100644 --- a/test/featuregates_linter/test_data/versioned_feature_list.yaml +++ b/test/featuregates_linter/test_data/versioned_feature_list.yaml @@ -888,6 +888,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 From b78f6e2fcc4dbda6e3631a5a54bf23b4c2561117 Mon Sep 17 00:00:00 2001 From: ndixita Date: Wed, 6 Nov 2024 18:07:09 +0000 Subject: [PATCH 14/15] Allowlisting k8s.io/components-helpers/resource import in ./pkg/apis/core --- staging/publishing/import-restrictions.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/staging/publishing/import-restrictions.yaml b/staging/publishing/import-restrictions.yaml index ff825b3c237c9..989721473e7a6 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. From b30e6c8b0e16100428e1925f6cefdd884a96cfb5 Mon Sep 17 00:00:00 2001 From: ndixita Date: Thu, 7 Nov 2024 21:28:57 +0000 Subject: [PATCH 15/15] keeping the qos code as-is for the existing case when pod-level resources are not set Signed-off-by: ndixita --- pkg/apis/core/helper/qos/qos.go | 36 ++++++++++++++++++++++++++---- pkg/apis/core/v1/helper/qos/qos.go | 36 ++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/pkg/apis/core/helper/qos/qos.go b/pkg/apis/core/helper/qos/qos.go index 2b0cdcfff6eb9..4f81d646b2252 100644 --- a/pkg/apis/core/helper/qos/qos.go +++ b/pkg/apis/core/helper/qos/qos.go @@ -113,11 +113,39 @@ func ComputePodQOS(pod *core.Pod) core.PodQOSClass { allContainers = append(allContainers, pod.Spec.InitContainers...) for _, container := range allContainers { // process requests - processResourceList(requests, container.Resources.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 - processResourceList(limits, container.Resources.Limits) - qosLimitResources := getQOSResources(container.Resources.Limits) - if !qosLimitResources.HasAll(string(core.ResourceMemory), string(core.ResourceCPU)) { + 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 } } diff --git a/pkg/apis/core/v1/helper/qos/qos.go b/pkg/apis/core/v1/helper/qos/qos.go index 66e512b16d080..b6ba7bf63ae37 100644 --- a/pkg/apis/core/v1/helper/qos/qos.go +++ b/pkg/apis/core/v1/helper/qos/qos.go @@ -116,11 +116,39 @@ func ComputePodQOS(pod *v1.Pod) v1.PodQOSClass { allContainers = append(allContainers, pod.Spec.InitContainers...) for _, container := range allContainers { // process requests - processResourceList(requests, container.Resources.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 - processResourceList(limits, container.Resources.Limits) - qosLimitResources := getQOSResources(container.Resources.Limits) - if !qosLimitResources.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) { + 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 } }