class MoveArrayLengthOnReferenceCastToLiteralCastInsideOperators implements Rule<Function> {

    private final Capture<Function> castCapture;
    private final Pattern<Function> pattern;
    private final FunctionSymbolResolver functionResolver;

    public MoveArrayLengthOnReferenceCastToLiteralCastInsideOperators(FunctionSymbolResolver functionResolver) {
        this.functionResolver = functionResolver;
        this.castCapture = new Capture<>();
        this.pattern = typeOf(Function.class)
            .with(f -> COMPARISON_OPERATORS.contains(f.name()))
            .with(f -> f.arguments().get(1).symbolType().isValueOrParameterSymbol())
            .with(f -> Optional.of(f.arguments().get(0)), typeOf(Function.class).capturedAs(castCapture)
                .with(f -> f.isCast())
                .with(f -> Optional.of(f.arguments().get(0)), typeOf(Function.class)
                    .with(f -> f.name().equals(ArrayUpperFunction.ARRAY_LENGTH)
                               || f.name().equals(ArrayUpperFunction.ARRAY_UPPER))
                    .with(f -> f.arguments().get(0).symbolType() == SymbolType.REFERENCE)
                )
            );
    }

    @Override
    public Pattern<Function> pattern() {
        return pattern;
    }

    @Override
    public Symbol apply(Function operator,
                        Captures captures,
                        NodeContext nodeCtx,
                        Symbol parentNode) {<FILL_FUNCTION_BODY>}
}

        var literalOrParam = operator.arguments().get(1);
        var castFunction = captures.get(castCapture);
        var function = castFunction.arguments().get(0);
        DataType<?> targetType = function.valueType();

        return functionResolver.apply(
            operator.name(),
            List.of(function, literalOrParam.cast(targetType))
        );
    
<no_super_class>