class AbstractIndexScan extends ProjectableFilterableTableScan {
    /** */
    protected final String idxName;

    /** */
    protected final List<SearchBounds> searchBounds;

    /**
     * Constructor used for deserialization.
     *
     * @param input Serialized representation.
     */
    protected AbstractIndexScan(RelInput input) {
        super(input);
        idxName = input.getString("index");
        searchBounds = ((RelInputEx)input).getSearchBounds("searchBounds");
    }

    /** */
    protected AbstractIndexScan(
        RelOptCluster cluster,
        RelTraitSet traitSet,
        RelOptTable table,
        String idxName,
        @Nullable List<RexNode> proj,
        @Nullable RexNode cond,
        @Nullable List<SearchBounds> searchBounds,
        @Nullable ImmutableBitSet reqColumns
    ) {
        super(cluster, traitSet, Collections.emptyList(), table, proj, cond, reqColumns);

        this.idxName = idxName;
        this.searchBounds = searchBounds;
    }

    /** {@inheritDoc} */
    @Override protected RelWriter explainTerms0(RelWriter pw) {
        pw = pw.item("index", idxName);
        pw = super.explainTerms0(pw);
        pw = pw.itemIf("searchBounds", searchBounds, searchBounds != null);

        if (pw.getDetailLevel() == SqlExplainLevel.ALL_ATTRIBUTES)
            pw = pw.item("inlineScan", isInlineScan());

        return pw;
    }

    /**
     *
     */
    public String indexName() {
        return idxName;
    }

    /** */
    public boolean isInlineScan() {
        IgniteTable tbl = table.unwrap(IgniteTable.class);
        IgniteIndex idx = tbl.getIndex(idxName);

        if (idx != null)
            return idx.isInlineScanPossible(requiredColumns);

        return false;
    }

    /** {@inheritDoc} */
    @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {<FILL_FUNCTION_BODY>}

    /** */
    public List<SearchBounds> searchBounds() {
        return searchBounds;
    }
}

        double rows = table.getRowCount();

        double cost;

        IgniteTable tbl = table.unwrap(IgniteTable.class);
        IgniteIndex idx = tbl.getIndex(idxName);

        double inlineReward = (idx != null && isInlineScan()) ?
            (0.5d + 0.5d * idx.collation().getFieldCollations().size() / table.getRowType().getFieldCount()) : 1d;

        if (condition == null)
            cost = rows * IgniteCost.ROW_PASS_THROUGH_COST * inlineReward;
        else {
            RexBuilder builder = getCluster().getRexBuilder();

            double selectivity = 1;

            cost = 0;

            if (searchBounds != null) {
                selectivity = mq.getSelectivity(this, RexUtil.composeConjunction(builder,
                        Commons.transform(searchBounds, b -> b == null ? null : b.condition())));

                cost = Math.log(rows) * IgniteCost.ROW_COMPARISON_COST;
            }

            rows *= selectivity;

            if (rows <= 0)
                rows = 1;

            cost += rows * (IgniteCost.ROW_COMPARISON_COST + IgniteCost.ROW_PASS_THROUGH_COST * inlineReward);
        }

        // additional tiny cost for preventing equality with table scan.
        return planner.getCostFactory().makeCost(rows, cost, 0).plus(planner.getCostFactory().makeTinyCost());
    
<methods>public RelColumnOrigin columnOriginsByRelLocalRef(int) ,public RelOptCost computeSelfCost(RelOptPlanner, RelMetadataQuery) ,public RexNode condition() ,public RelNode copy(RelTraitSet, List<RelNode>) ,public RelDataType deriveRowType() ,public double estimateRowCount(RelMetadataQuery) ,public RelWriter explainTerms(RelWriter) ,public List<RexNode> projects() ,public RexNode pushUpPredicate() ,public ImmutableBitSet requiredColumns() <variables>protected final non-sealed RexNode condition,protected final non-sealed List<RexNode> projects,protected final non-sealed ImmutableBitSet requiredColumns