/// <summary>
        /// Computes the types (column and slotnames), the length reduction, categorical feature indices
        /// and whether the column is suppressed.
        /// The slotsMin and slotsMax arrays should be sorted and the intervals should not overlap.
        /// </summary>
        /// <param name="input">The input schema</param>
        /// <param name="slotsMin">The beginning indices of the ranges of slots to be dropped</param>
        /// <param name="slotsMax">The end indices of the ranges of slots to be dropped</param>
        /// <param name="iinfo">The column index in Infos</param>
        /// <param name="slotDropper">The slots to be dropped.</param>
        /// <param name="suppressed">Whether the column is suppressed (all slots dropped)</param>
        /// <param name="type">The column type</param>
        /// <param name="categoricalRanges">Categorical feature indices.</param>
        private void ComputeType(Schema input, int[] slotsMin, int[] slotsMax, int iinfo,
                                 SlotDropper slotDropper, out bool suppressed, out ColumnType type, out int[] categoricalRanges)
        {
            Contracts.AssertValue(slotDropper);
            Contracts.AssertValue(input);
            Contracts.AssertNonEmpty(slotsMin);
            Contracts.AssertNonEmpty(slotsMax);
            Contracts.Assert(slotsMin.Length == slotsMax.Length);
            Contracts.Assert(0 <= iinfo && iinfo < Infos.Length);

            categoricalRanges = null;
            // Register for metadata. Propagate the IsNormalized metadata.
            using (var bldr = Metadata.BuildMetadata(iinfo, input, Infos[iinfo].Source,
                                                     MetadataUtils.Kinds.IsNormalized, MetadataUtils.Kinds.KeyValues))
            {
                var typeSrc = Infos[iinfo].TypeSrc;
                if (!typeSrc.IsVector)
                {
                    type       = typeSrc;
                    suppressed = slotsMin.Length > 0 && slotsMin[0] == 0;
                }
                else if (!typeSrc.IsKnownSizeVector)
                {
                    type       = typeSrc;
                    suppressed = false;
                }
                else
                {
                    Host.Assert(typeSrc.IsKnownSizeVector);
                    var dstLength    = slotDropper.DstLength;
                    var hasSlotNames = input.HasSlotNames(Infos[iinfo].Source, Infos[iinfo].TypeSrc.VectorSize);
                    type       = new VectorType(typeSrc.ItemType.AsPrimitive, Math.Max(dstLength, 1));
                    suppressed = dstLength == 0;

                    if (hasSlotNames && dstLength > 0)
                    {
                        // Add slot name metadata.
                        bldr.AddGetter <VBuffer <ReadOnlyMemory <char> > >(MetadataUtils.Kinds.SlotNames,
                                                                           new VectorType(TextType.Instance, dstLength), GetSlotNames);
                    }
                }

                if (!suppressed)
                {
                    if (MetadataUtils.TryGetCategoricalFeatureIndices(Source.Schema, Infos[iinfo].Source, out categoricalRanges))
                    {
                        VBuffer <int> dst = default(VBuffer <int>);
                        GetCategoricalSlotRangesCore(iinfo, slotDropper.SlotsMin, slotDropper.SlotsMax, categoricalRanges, ref dst);
                        // REVIEW: cache dst as opposed to caculating it again.
                        if (dst.Length > 0)
                        {
                            Contracts.Assert(dst.Length % 2 == 0);

                            bldr.AddGetter <VBuffer <int> >(MetadataUtils.Kinds.CategoricalSlotRanges,
                                                            MetadataUtils.GetCategoricalType(dst.Length / 2), GetCategoricalSlotRanges);
                        }
                    }
                }
            }
        }