Package r.nodes.exec

Source Code of r.nodes.exec.ReadVector

package r.nodes.exec;

import r.*;
import r.data.*;
import r.data.RAny.*;
import r.data.RArray.Names;
import r.data.internal.*;
import r.data.internal.IntImpl.RIntSequence;
import r.errors.*;
import r.nodes.ast.*;
import r.runtime.*;

// FIXME: add check for the number of dimensions in index
// FIXME: probably should also specialize for base types
// FIXME: get get a bit more performance by joining failure modes when distinction is not necessary (and particularly when that makes the checks easier)
//        i've done this with SimpleScalarDoubleSelection, and it helped a bit

// FIXME: consider adding Select specialization to SimpleIntScalarSelection, but now disabled even in SimpleDoubleScalarSelection as it is not good for the
//        binarytrees benchmark

// FIXME: add more support for constant vector indices
// FIXME: check for scalar types instead of type & size (in SimpleScalarXXX)

// rewriting of vector selection nodes:
//
// DoubleBaseSimpleSelection.ScalarDoubleSelection -> DoubleBaseSimpleSelection.ScalarIntSelection -> (ctd below)
//
// >SimpleScalarIntSelection   -> SimpleScalarDoubleSelection -> GenericScalarSelection -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> GenericScalarSelection -> SimpleLogicalSelection -> LogicalSelection -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> GenericScalarSelection -> SimpleLogicalSelection -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> GenericScalarSelection -> Subscript -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> Subscript -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> SimpleScalarStringSelection -> GenericScalarSelection -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> SimpleScalarStringSelection -> GenericScalarSelection -> SimpleLogicalSelection -> LogicalSelection -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> SimpleScalarStringSelection -> GenericScalarSelection -> SimpleLogicalSelection -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> SimpleScalarStringSelection -> GenericScalarSelection -> Subscript -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> SimpleScalarStringSelection -> Subscript -> GenericSelection
//                             -> SimpleScalarDoubleSelection -> SimpleScalarStringSelection -> StringSelection
//                             -> GenericScalarSelection -> Subscript -> GenericSelection
//                             -> GenericScalarSelection -> GenericSelection
//                             -> SimpleIntSequenceSelection -> (below)
//                             -> IntSelection -> GenericSelection
//                             -> Subscript -> GenericSelection
//                             -
// *SimpleIntSimpleRangeSelection -> SimpleIntSequenceSelection -> IntSelection -> GenericSelection
//                             -> GenericSelection
//
public abstract class ReadVector extends BaseR {

    @Child RNode lhs;
    @Children final RNode[] indexes;
    final boolean subset;

    private static final boolean DEBUG_SEL = false;

    ReadVector(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
        super(ast);
        this.lhs = adoptChild(lhs);
        this.indexes = adoptChildren(indexes);
        this.subset = subset;
    }

    enum Failure {
        NOT_ARRAY_BASE,
        NOT_ARRAY_INDEX,
        NOT_INT_INDEX,
        NOT_INT_SEQUENCE_INDEX,
        NOT_DOUBLE_INDEX,
        NOT_STRING_INDEX,
        NOT_ALL_POSITIVE_INDEX,
        NOT_INT_OR_DOUBLE_INDEX,
        NOT_LOGICAL_INDEX,
        NOT_ONE_ELEMENT,
        NA_INDEX,
        NOT_POSITIVE_INDEX,
        INDEX_OUT_OF_BOUNDS,
        NOT_SAME_LENGTH,
        NOT_SUBSET,
        BASE_HAS_NAMES,
        UNSPECIFIED
    }

    @Override public Object execute(Frame frame) {
        if (getNewNode() != null) {
            System.err.println("Shit...\n");
        }

        assert Utils.check(getNewNode() == null);
        RAny base = (RAny) lhs.execute(frame); // note: order is important
        if (getNewNode() != null) {
            return ((ReadVector)getNewNode()).executeWithBase(frame, base);
        }
        return executeWithBase(frame, base);
    }

    public Object executeWithBase(Frame frame, RAny base) {
        RAny index = (RAny) indexes[0].execute(frame);
        if (getNewNode() != null) {
            return ((ReadVector)getNewNode()).execute(index, base);
        }
        return execute(index, base);
    }

    @Override
    protected <N extends RNode> N replaceChild(RNode oldNode, N newNode) {
        assert oldNode != null;
        if (lhs == oldNode) {
            lhs = newNode;
            return adoptInternal(newNode);
        }
        if (indexes != null) {
            for(int i = 0; i < indexes.length; i++) {
                if (indexes[i] == oldNode) {
                    indexes[i] = newNode;
                    return adoptInternal(newNode);
                }
            }
        }
        return super.replaceChild(oldNode, newNode);
    }

    abstract RAny execute(RAny index, RAny vector);

    // NOTE: currently not used
    private abstract static class Select {
        abstract RAny select(RAny vector, int index) throws SpecializationException;
    }

    // NOTE: currently not used
    @SuppressWarnings("unused") private static Select createScalarSelect(boolean subset, RAny vectorTemplate) {
        if (subset) {
            return new Select() {
                @Override final RAny select(RAny vector, int index) throws SpecializationException {
                    if (!(vector instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                    RArray vrarr = (RArray) vector;
                    if (index > vrarr.size()) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                    return vrarr.boxedGet(index - 1);
                }
            };
        } else if (!(vectorTemplate instanceof RList)) {
            return new Select() {
                @Override final RAny select(RAny vector, int index) throws SpecializationException {
                    if (vector instanceof RList || !(vector instanceof RArray)) { throw new SpecializationException(Failure.UNSPECIFIED); }
                    RArray vrarr = (RArray) vector;
                    if (index > vrarr.size()) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                    return vrarr.boxedGet(index - 1);
                }
            };
        } else {
            return new Select() {
                @Override final RAny select(RAny vector, int index) throws SpecializationException {
                    if (!(vector instanceof RList)) { throw new SpecializationException(Failure.UNSPECIFIED); }
                    RList vlist = (RList) vector;
                    if (index > vlist.size()) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                    return vlist.getRAny(index - 1);
                }
            };
        }
    }

    // when the index has only one argument, which is an integer
    //   for more complicated and corner cases rewrites itself
    public static class SimpleScalarIntSelection extends ReadVector {

        public SimpleScalarIntSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
        }

        @Override public RAny execute(RAny index, RAny vector) {
            try {
                if (!(vector instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                if (!(index instanceof RInt)) { throw new SpecializationException(Failure.NOT_INT_INDEX); }
                RArray vrarr = (RArray) vector;
                if (vrarr.names() != null) { throw new SpecializationException(Failure.UNSPECIFIED); }
                RInt irint = (RInt) index;
                if (irint.size() != 1) { throw new SpecializationException(Failure.NOT_ONE_ELEMENT); }
                int i = irint.getInt(0);
                if (i <= 0 || i > vrarr.size()) { throw new SpecializationException(Failure.UNSPECIFIED); }// includes NA_INDEX, NOT_POSITIVE_INDEX, INDEX_OUT_OF_BOUNDS
                if (subset || !(vrarr instanceof RList)) {
                    return vrarr.boxedGet(i - 1);
                } else {
                    return ((RList) vrarr).getRAny(i - 1);
                }
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - SimpleScalarIntSelection failed: " + f);
                switch (f) {
                case NOT_INT_INDEX:
                    SimpleScalarDoubleSelection dbl = new SimpleScalarDoubleSelection(ast, lhs, indexes, subset, vector);
                    replace(dbl, "install SimpleScalarDoubleSelection from SimpleScalarIntSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with SimpleScalarDoubleSelection");
                    return dbl.execute(index, vector);

                case NOT_ONE_ELEMENT:
                    if (subset) {
                        if (IntImpl.RIntSimpleRange.isInstance(index)) {
                            SimpleIntSimpleRangeSelection is = new SimpleIntSimpleRangeSelection(ast, lhs, indexes, subset);
                            replace(is, "install SimpleIntSimpleRangeSelection from SimpleScalarIntSelection");
                            if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with SimpleIntSimpleRangeSelection");
                            return is.execute(index, vector);
                        } else if (IntImpl.RIntSequence.isInstance(index)) {
                            SimpleIntSequenceSelection is = new SimpleIntSequenceSelection(ast, lhs, indexes, subset);
                            replace(is, "install SimpleIntSequenceSelection from SimpleScalarIntSelection");
                            if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with SimpleIntSequenceSelection");
                            return is.execute(index, vector);
                        } else {
                            IntSelection is = new IntSelection(ast, lhs, indexes, subset);
                            replace(is, "install IntSelection from SimpleScalarIntSelection");
                            if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with IntSelection");
                            return is.execute(index, vector);
                        }
                    } else {
                        Subscript s = new Subscript(ast, lhs, indexes, subset);
                        replace(s, "install Subscript from SimpleScalarIntSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with Subscript");
                        return s.execute(index, vector);
                    }

                default:
                    GenericScalarSelection gen = new GenericScalarSelection(ast, lhs, indexes, subset);
                    replace(gen, "install GenericScalarSelection from SimpleScalarIntSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericScalarSelection");
                    return gen.execute(index, vector);

                }
            }
        }
    }

    // when the index has only one argument, which is a constant integer (so can use for double, too, with a cast)
    //   for more complicated and corner cases rewrites itself
    public static class SimpleConstantScalarIntSelection extends ReadVector {

        final int index;

        public SimpleConstantScalarIntSelection(ASTNode ast, RNode lhs, RNode[] indexes, int index, boolean subset) {
            super(ast, lhs, indexes, subset);
            this.index = index;
        }

        @Override public Object execute(Frame frame) {
            assert Utils.check(getNewNode() == null);
            RAny base = (RAny) lhs.execute(frame);
            if (getNewNode() != null) {
                return ((ReadVector.SimpleConstantScalarIntSelection)getNewNode()).executeWithBase(frame, base);
            }
            return executeWithBase(frame, base);
        }

        @Override
        public Object executeWithBase(Frame frame, RAny base) {
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray vrarr = (RArray) base;
                if (index > vrarr.size()) { throw new SpecializationException(Failure.UNSPECIFIED); // includes NA_INDEX, NOT_POSITIVE_INDEX, INDEX_OUT_OF_BOUNDS
                }
                return getWithName(vrarr, index - 1, subset);
            } catch (SpecializationException e) {
                RNode theIndex0 = indexes[0];
                GenericScalarSelection gen = new GenericScalarSelection(ast, lhs, indexes, subset);
                replace(gen, "");
                return gen.execute((RAny) theIndex0.execute(frame), base);
            }
        }

        @Override
        public RAny execute(RAny idx, RAny vector) {
            return null;
        }
    }

    // when the base is DoubleImpl and the index is a scalar*impl (and a simple case, e.g. within bounds)
    public abstract static class DoubleBaseSimpleSelection extends ReadVector {
        public DoubleBaseSimpleSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
        }

        public static class ScalarIntSelection extends DoubleBaseSimpleSelection {
            public ScalarIntSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
                super(ast, lhs, indexes, subset);
            }

            @Override public RAny execute(RAny index, RAny vector) {
                try {
                    if (!(index instanceof ScalarIntImpl)) { throw new SpecializationException(null); }
                    int i = (((ScalarIntImpl) index).getInt()) - 1;
                    if (!(vector instanceof DoubleImpl)) { throw new SpecializationException(null); }
                    DoubleImpl dbase = (DoubleImpl) vector;
                    double[] base = dbase.getContent();
                    if (i < 0 || i >= base.length || dbase.names() != null) { throw new SpecializationException(null); }
                    return RDouble.RDoubleFactory.getScalar(base[i]);

                } catch (SpecializationException e) {
                    ScalarDoubleSelection ns = new ScalarDoubleSelection(ast, lhs, indexes, subset);
                    replace(ns, "install DoubleBaseSimpleSelection.ScalarDoubleSelection from DoubleBaseSimpleSelection.ScalarIntSelection");
                    return ns.execute(index, vector);
                }
            }
        }

        public static class ScalarDoubleSelection extends DoubleBaseSimpleSelection {
            public ScalarDoubleSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
                super(ast, lhs, indexes, subset);
            }

            @Override public RAny execute(RAny index, RAny vector) {
                try {
                    if (!(index instanceof ScalarDoubleImpl)) { throw new SpecializationException(null); }
                    int i = Convert.double2int(((ScalarDoubleImpl) index).getDouble()) - 1;
                    if (!(vector instanceof DoubleImpl)) { throw new SpecializationException(null); }
                    DoubleImpl dbase = (DoubleImpl) vector;
                    double[] base = dbase.getContent();
                    if (i < 0 || i >= base.length || dbase.names() != null) { throw new SpecializationException(null); }
                    return RDouble.RDoubleFactory.getScalar(base[i]);

                } catch (SpecializationException e) {
                    SimpleScalarIntSelection is = new SimpleScalarIntSelection(ast, lhs, indexes, subset);
                    replace(is, "install SimpleScalarIntSelection from DoubleBaseSimpleSelection.ScalarIntSelection");
                    return is.execute(index, vector);
                }
            }
        }
    }

    // when the index has only one argument, which is a double
    //   for more complicated and corner cases rewrites itself
    public static class SimpleScalarDoubleSelection extends ReadVector {
        //final Select select;
        public SimpleScalarDoubleSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset, @SuppressWarnings("unused") RAny vectorTemplate) {
            super(ast, lhs, indexes, subset);
            //this.select = createScalarSelect(subset, vectorTemplate);
        }

        @Override public RAny execute(RAny index, RAny vector) {
            if (DEBUG_SEL) Utils.debug("selection - executing SimpleScalarDoubleSelection");
            try {
                if (!(index instanceof RDouble)) { throw new SpecializationException(Failure.NOT_DOUBLE_INDEX); }
                RDouble irdbl = (RDouble) index;
                if (irdbl.size() != 1) { throw new SpecializationException(Failure.NOT_ONE_ELEMENT); }
                int i = Convert.double2int(irdbl.getDouble(0)); // FIXME: check when the index is too large

                // FIXME: surprisingly using select did not work well in binarytrees benchmark, where the base alternates between list and integer
                //                if (i <= 0) {
                //                    throw new UnexpectedResultException(Failure.NOT_POSITIVE_INDEX); // includes NA_INDEX
                //                }
                //                return select.select(vector, i);

                if (!(vector instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray vrarr = (RArray) vector;
                if (i <= 0 || i > vrarr.size() || vrarr.names() != null) { throw new SpecializationException(Failure.UNSPECIFIED);
                // includes NA_INDEX, NOT_POSITIVE_INDEX, INDEX_OUT_OF_BOUNDS, has names
                }
                if (subset || !(vrarr instanceof RList)) {
                    return vrarr.boxedGet(i - 1);
                } else {
                    return ((RList) vrarr).getRAny(i - 1);
                }
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - SimpleScalarDoubleSelection failed: " + f);
                switch (f) {
                case NOT_ONE_ELEMENT:
                    if (subset) {
                        IntSelection is = new IntSelection(ast, lhs, indexes, subset);
                        replace(is, "install IntSelection from SimpleScalarDoubleSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with IntSelection");
                        return is.execute(index, vector);
                    } else {
                        Subscript s = new Subscript(ast, lhs, indexes, subset);
                        replace(s, "install Subscript from SimpleScalarDoubleSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with Subscript");
                        return s.execute(index, vector);
                    }
                case NOT_DOUBLE_INDEX:
                    if (index instanceof RString) {
                        SimpleScalarStringSelection ss = new SimpleScalarStringSelection(ast, lhs, indexes, subset, vector);
                        replace(ss, "install SimpleScalarStringSelection from SimpleScalarDoubleSelection");
                        return ss.execute(index, vector);
                    }
                default:
                    GenericScalarSelection gen = new GenericScalarSelection(ast, lhs, indexes, subset);
                    replace(gen, "install GenericScalarSelection from SimpleScalarDoubleSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericScalarSelection");
                    return gen.execute(index, vector);
                }
            }
        }
    }

    // when the index has only one argument, which is a string
    public static class SimpleScalarStringSelection extends ReadVector {
        public SimpleScalarStringSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset, @SuppressWarnings("unused") RAny vectorTemplate) {
            super(ast, lhs, indexes, subset);
        }

        @Override public RAny execute(RAny index, RAny vector) {
            if (DEBUG_SEL) Utils.debug("selection - executing SimpleScalarStringSelection");
            try {
                if (!(index instanceof RString)) { throw new SpecializationException(Failure.NOT_STRING_INDEX); }
                RString irstr = (RString) index;
                if (irstr.size() != 1) { throw new SpecializationException(Failure.NOT_ONE_ELEMENT); }
                if (!(vector instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray vrarr = (RArray) vector;
                Names names = vrarr.names();
                if (names == null) { throw new SpecializationException(Failure.UNSPECIFIED); }
                RSymbol symbol = RSymbol.getSymbol(irstr.getString(0));
                int i = names.map(symbol);
                if (i == -1) { throw new SpecializationException(Failure.UNSPECIFIED); }
                return getWithName(vrarr, i, subset);
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - SimpleScalarStringSelection failed: " + f);
                switch (f) {
                case NOT_ONE_ELEMENT:
                    if (subset) {
                        StringSelection is = new StringSelection(ast, lhs, indexes, subset);
                        replace(is, "install StringSelection from SimpleScalarStringSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with IntSelection");
                        return is.execute(index, vector);
                    } else {
                        Subscript s = new Subscript(ast, lhs, indexes, subset);
                        replace(s, "install Subscript from SimpleScalarStringSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with Subscript");
                        return s.execute(index, vector);
                    }
                default:
                    GenericScalarSelection gen = new GenericScalarSelection(ast, lhs, indexes, subset);
                    replace(gen, "install GenericScalarSelection from SimpleScalarStringSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericScalarSelection");
                    return gen.execute(index, vector);
                }
            }
        }
    }

    private static RAny getWithName(RArray base, int i, boolean subset) {
        if (subset) {
            return base.boxedNamedGet(i);
        } else if (base instanceof RList) {
            return ((RList) base).getRAny(i); // list subscript does not preserve names
        } else {
            return base.boxedGet(i); // non-list subscript does not preserve names, either
        }
    }

    // any case when the index has only one argument (a scalar)
    //   rewrites itself when index is not a single scalar
    public static class GenericScalarSelection extends ReadVector {
        public GenericScalarSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
        }

        // index must be a scalar
        public static RAny executeScalar(RArray base, RArray index, boolean subset, ASTNode ast) {
            int i = 0;
            int size = base.size();
            if (index instanceof RDouble) {
                RDouble idbl = (RDouble) index;
                i = Convert.double2int(idbl.getDouble(0)); // FIXME: check when the index is too large
            } else if (index instanceof RInt) {
                i = ((RInt) index).getInt(0);
            } else if (index instanceof RLogical) {
                i = ((RLogical) index).getLogical(0);
                if (subset) {
                    if (i == RLogical.TRUE) { return base; }
                    if (i == RLogical.FALSE) { return Utils.createEmptyArray(base); }
                }
            } else if (index instanceof RString) {
                RSymbol name = RSymbol.getSymbol(((RString) index).getString(0));
                Names bnames = base.names();
                if (bnames == null) { return Utils.getBoxedNA(base); }
                i = bnames.map(name);
                if (i != -1) {
                    return getWithName(base, i, subset);
                } else {
                    return Utils.getNamedNA(base);
                }
            } else {
                throw RError.getInvalidSubscriptType(ast, index.typeOf());
            }

            if (i > 0) { // NOTE: RInt.NA < 0
                if (i <= size) { return getWithName(base, i - 1, subset); }
            }

            if (size == 0) { return Utils.createNA(base); }
            if (!subset) {
                if (i == 0) { throw RError.getSelectLessThanOne(ast); }
                if (i > 0 || i == RInt.NA) { // means also i > size
                    throw RError.getSubscriptBounds(ast);
                }
                // i < 0
                if (size > 2) { throw RError.getSelectMoreThanOne(ast); }
                if (size == 1) { throw RError.getSelectLessThanOne(ast); }
                // size == 2
                if (i != -1 && i != -2) { throw RError.getSelectMoreThanOne(ast); }
            }
            if (base instanceof RDouble) { // FIXME: could reduce verbosity through refactoring the number factories?
                if (i == RInt.NA) { return RDouble.RDoubleFactory.getNAArray(size); }
                if (i < 0) {
                    if (-i > size) { return base.stripAttributesKeepNames(); }
                    return RDouble.RDoubleFactory.exclude(-i - 1, (RDouble) base);
                }
                if (i == 0) { return RDouble.RDoubleFactory.getEmpty(base.names() != null); }
                // i > size
                return RDouble.RDoubleFactory.getNA(base.names() != null);
            }
            if (base instanceof RInt) {
                if (i == RInt.NA) { return RInt.RIntFactory.getNAArray(size); }
                if (i < 0) {
                    if (-i > size) { return base.stripAttributesKeepNames(); }
                    return RInt.RIntFactory.exclude(-i - 1, (RInt) base);
                }
                if (i == 0) { return RInt.RIntFactory.getEmpty(base.names() != null); }
                // i > size
                return RInt.RIntFactory.getNA(base.names() != null);
            }
            if (base instanceof RLogical) {
                if (i == RInt.NA) { return RLogical.RLogicalFactory.getNAArray(size); }
                if (i < 0) {
                    if (-i > size) { return base.stripAttributesKeepNames(); }
                    return RLogical.RLogicalFactory.exclude(-i - 1, (RLogical) base);
                }
                if (i == 0) { return RLogical.RLogicalFactory.getEmpty(base.names() != null); }
                // i > size
                return RLogical.RLogicalFactory.getNA(base.names() != null);
            }
            if (base instanceof RList) {
                if (i == RInt.NA) { return RList.RListFactory.getNullArray(size); }
                if (i < 0) {
                    if (-i > size) { return base.stripAttributesKeepNames(); }
                    return RList.RListFactory.exclude(-i - 1, (RList) base);
                }
                if (i == 0) { return RList.RListFactory.getEmpty(base.names() != null); }
                // i > size
                return RList.RListFactory.getNull(base.names() != null);
            }
            if (base instanceof RString) {
                if (i == RInt.NA) { return RString.RStringFactory.getNAArray(size); }
                if (i < 0) {
                    if (-i > size) { return base.stripAttributesKeepNames(); }
                    return RString.RStringFactory.exclude(-i - 1, (RString) base);
                }
                if (i == 0) { return RString.RStringFactory.getEmpty(base.names() != null); }
                // i > size
                return RString.RStringFactory.getNA(base.names() != null);
            }
            if (base instanceof RComplex) {
                if (i == RInt.NA) { return RComplex.RComplexFactory.getNAArray(size); }
                if (i < 0) {
                    if (-i > size) { return base.stripAttributesKeepNames(); }
                    return RComplex.RComplexFactory.exclude(-i - 1, (RComplex) base);
                }
                if (i == 0) { return RComplex.RComplexFactory.getEmpty(base.names() != null); }
                // i > size
                return RComplex.RComplexFactory.getNA(base.names() != null);
            }
            assert Utils.check(base instanceof RRaw);
            if (i < 0) {
                if (-i > size) { return base.stripAttributesKeepNames(); }
                return RRaw.RRawFactory.exclude(-i - 1, (RRaw) base);
            }
            if (i == 0) { return RRaw.RRawFactory.getEmpty(base.names() != null); }
            // i > size
            return RRaw.RRawFactory.getZero(base.names() != null);
        }

        @Override public RAny execute(RAny index, RAny vector) {
            if (DEBUG_SEL) Utils.debug("selection - executing GenericScalarSelection");
            try {
                if (!(vector instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray vrarr = (RArray) vector;
                if (!(index instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_INDEX); }
                RArray irarr = (RArray) index;
                if (irarr.size() != 1) { throw new SpecializationException(Failure.NOT_ONE_ELEMENT); }
                return executeScalar(vrarr, irarr, subset, ast);

            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - GenericScalarSelection failed: " + f);

                if (f == Failure.NOT_ONE_ELEMENT && index instanceof RLogical && subset) {
                    SimpleLogicalSelection ls = new SimpleLogicalSelection(ast, lhs, indexes, subset);
                    replace(ls, "install SimpleLogicalSelection from GenericScalarSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with SimpleLogicalSelection");
                    return ls.execute(index, vector);
                } else {
                    if (!subset) {
                        Subscript s = new Subscript(ast, lhs, indexes, subset);
                        replace(s, "install Subscript from GenericScalarSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with Subscript");
                        return s.execute(index, vector);
                    } else {
                        GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                        replace(gs, "install GenericSelection from GenericScalarSelection");
                        if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                        return gs.execute(index, vector);
                    }
                }
            }
        }
    }

// when the index is a sequence of integers (e.g. created using the colon operator)
    //   rewrites itself for other and corner cases
    public static class SimpleIntSequenceSelection extends ReadVector {
        public SimpleIntSequenceSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(subset);
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing SimpleIntSequenceSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (abase.names() != null) { throw new SpecializationException(Failure.BASE_HAS_NAMES); // FIXME: lazy names?
                }
                if (!IntImpl.RIntSequence.isInstance(index)) { // FIXME: this goes directly to Int implementation, not terribly nice
                    throw new SpecializationException(Failure.NOT_INT_SEQUENCE_INDEX);
                }
                IntImpl.RIntSequence sindex = IntImpl.RIntSequence.cast(index);

                if (!sindex.isPositive()) { throw new SpecializationException(Failure.NOT_ALL_POSITIVE_INDEX); }
                int size = abase.size();
                if (sindex.max() > size) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                // FIXME: should specialize for a particular base type, or have a type hierarchy on factories
                if (abase instanceof RDouble) { return TracingView.ViewTrace.trace(new RDoubleView((RDouble) abase, sindex.from(), sindex.to(), sindex.step())); }
                if (abase instanceof RInt) { return TracingView.ViewTrace.trace(new RIntView((RInt) abase, sindex.from(), sindex.to(), sindex.step())); }
                if (abase instanceof RLogical) { return TracingView.ViewTrace.trace(new RLogicalView((RLogical) abase, sindex.from(), sindex.to(), sindex.step())); }
                if (abase instanceof RList) { return TracingView.ViewTrace.trace(new RListView((RList) abase, sindex.from(), sindex.to(), sindex.step())); }
                if (abase instanceof RString) { return TracingView.ViewTrace.trace(new RStringView((RString) abase, sindex.from(), sindex.to(), sindex.step())); }
                if (abase instanceof RRaw) { return TracingView.ViewTrace.trace(new RRawView((RRaw) abase, sindex.from(), sindex.to(), sindex.step())); }
                assert Utils.check(abase instanceof RComplex);
                return TracingView.ViewTrace.trace(new RComplexView((RComplex) abase, sindex.from(), sindex.to(), sindex.step()));
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - SimpleIntSequenceSelection failed: " + f);
                switch (f) {
                case NOT_INT_SEQUENCE_INDEX:
                case NOT_ALL_POSITIVE_INDEX:
                case INDEX_OUT_OF_BOUNDS:
                case BASE_HAS_NAMES:
                    IntSelection is = new IntSelection(ast, lhs, indexes, subset);
                    replace(is, "install IntSelection from SimpleIntSequenceSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with IntSelection");
                    return is.execute(index, base);

                default:
                    GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                    replace(gs, "install GenericSelection from SimpleIntSequenceSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                    return gs.execute(index, base);
                }
            }
        }

        static class RRawView extends View.RRawProxy<RRaw> implements RRaw {
            final int from;
            final int to;
            final int step;
            final int size;

            public RRawView(RRaw base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public byte getRaw(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getRaw(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RLogicalView extends View.RLogicalProxy<RLogical> implements RLogical {
            final int from;
            final int to;
            final int step;
            final int size;

            public RLogicalView(RLogical base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public int getLogical(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getLogical(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RIntView extends View.RIntProxy<RInt> implements RInt {
            final int from;
            final int to;
            final int step;
            final int size;

            public RIntView(RInt base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public int getInt(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getInt(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RDoubleView extends View.RDoubleProxy<RDouble> implements RDouble {
            final int from;
            final int to;
            final int step;
            final int size;

            public RDoubleView(RDouble base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public double getDouble(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getDouble(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RComplexView extends View.RComplexProxy<RComplex> implements RComplex {
            final int from;
            final int to;
            final int step;
            final int size;

            public RComplexView(RComplex base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public double getReal(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getReal(from + i * step - 1);
            }

            @Override public double getImag(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getImag(from + i * step - 1);
            }

            @Override public Complex getComplex(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getComplex(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RStringView extends View.RStringProxy<RString> implements RString {
            final int from;
            final int to;
            final int step;
            final int size;

            public RStringView(RString base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public String getString(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getString(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RListView extends View.RListProxy<RList> implements RList {
            final int from;
            final int to;
            final int step;
            final int size;

            public RListView(RList base, int from, int to, int step) {
                super(base);
                this.from = from;
                this.to = to;
                this.step = step;
                this.size = RIntSequence.sequenceSize(from, to, step);
            }

            @Override public int size() {
                return size;
            }

            @Override public RAny getRAny(int i) {
                assert Utils.check(i < size, "bounds check");
                assert Utils.check(i >= 0, "bounds check");
                return orig.getRAny(from + i * step - 1);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }
    }

    // when the index is simple range of integers (e.g. created using the colon operator)
    //   rewrites itself for other and corner cases
    public static class SimpleIntSimpleRangeSelection extends ReadVector {
        public SimpleIntSimpleRangeSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(subset);
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing SimpleIntSimpleRangeSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (abase.names() != null) { throw new SpecializationException(Failure.BASE_HAS_NAMES); // FIXME: lazy names?
                }
                if (!IntImpl.RIntSimpleRange.isInstance(index)) {
                    throw new SpecializationException(Failure.NOT_INT_SEQUENCE_INDEX);
                }
                IntImpl.RIntSimpleRange sindex = IntImpl.RIntSimpleRange.cast(index);

                int size = abase.size();
                int indexTo = sindex.to();
                if (indexTo > size) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                // FIXME: should specialize for a particular base type, or have a type hierarchy on factories
                if (abase instanceof RDouble) { return TracingView.ViewTrace.trace(new RDoubleView((RDouble) abase, indexTo)); }
                if (abase instanceof RInt) { return TracingView.ViewTrace.trace(new RIntView((RInt) abase, indexTo)); }
                if (abase instanceof RLogical) { return TracingView.ViewTrace.trace(new RLogicalView((RLogical) abase, indexTo)); }
                if (abase instanceof RList) { return TracingView.ViewTrace.trace(new RListView((RList) abase, indexTo)); }
                if (abase instanceof RString) { return TracingView.ViewTrace.trace(new RStringView((RString) abase, indexTo)); }
                if (abase instanceof RRaw) { return TracingView.ViewTrace.trace(new RRawView((RRaw) abase, indexTo)); }
                assert Utils.check(abase instanceof RComplex);
                return TracingView.ViewTrace.trace(new RComplexView((RComplex) abase, indexTo));
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - SimpleIntSimpleRangeSelection failed: " + f);
                switch (f) {
                case NOT_INT_SEQUENCE_INDEX:
                    SimpleIntSequenceSelection iss = new SimpleIntSequenceSelection(ast, lhs, indexes, subset);
                    replace(iss, "install SimpleIntSequenceSelection from SimpleIntSimpleRangeSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with SimpleIntSequenceSelection");
                    return iss.execute(index, base);

                case NOT_ALL_POSITIVE_INDEX:
                case INDEX_OUT_OF_BOUNDS:
                case BASE_HAS_NAMES:
                    IntSelection is = new IntSelection(ast, lhs, indexes, subset);
                    replace(is, "install IntSelection from SimpleIntSimpleRangeSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with IntSelection");
                    return is.execute(index, base);

                default:
                    GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                    replace(gs, "install GenericSelection from SimpleIntSimpleRangeSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                    return gs.execute(index, base);
                }
            }
        }

        static class RRawView extends View.RRawProxy<RRaw> implements RRaw {
            final int to;

            public RRawView(RRaw base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public byte getRaw(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getRaw(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RLogicalView extends View.RLogicalProxy<RLogical> implements RLogical {
            final int to;

            public RLogicalView(RLogical base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public int getLogical(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getLogical(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RIntView extends View.RIntProxy<RInt> implements RInt {
            final int to;

            public RIntView(RInt base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public int getInt(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getInt(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RDoubleView extends View.RDoubleProxy<RDouble> implements RDouble {
            final int to;

            public RDoubleView(RDouble base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public double getDouble(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getDouble(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RComplexView extends View.RComplexProxy<RComplex> implements RComplex {
            final int to;

            public RComplexView(RComplex base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public double getReal(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getReal(i);
            }

            @Override public double getImag(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getImag(i);
            }

            @Override public Complex getComplex(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getComplex(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RStringView extends View.RStringProxy<RString> implements RString {
            final int to;

            public RStringView(RString base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public String getString(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getString(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }

        static class RListView extends View.RListProxy<RList> implements RList {
            final int to;

            public RListView(RList base, int to) {
                super(base);
                this.to = to;
            }

            @Override public int size() {
                return to;
            }

            @Override public RAny getRAny(int i) {
                assert Utils.check(i < to, "bounds check");
                return orig.getRAny(i);
            }

            @Override public int[] dimensions() { // drop dimensions
                return null;
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }
        }
    }

    // when the index is a vector of integers or doubles (selection by numeric index)
    //   casts double index to integer
    //   rewrites itself for other cases
    public static class IntSelection extends ReadVector { // FIXME: create yet another node without the negatives and zero crap
        public IntSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(subset);
        }

        public static RAny executeIntVector(RInt indexArg, RArray base, ASTNode ast) {

            Names names = base.names();
            RSymbol[] symbols = (names == null) ? null : names.sequence();
            RSymbol[] newSymbols = null;
            int nzeros = 0;
            boolean hasNegative = false;
            boolean hasPositive = false;
            boolean hasNA = false;
            int bsize = base.size();

            RInt index;
            if (indexArg instanceof View.ParametricView) {
                index = indexArg.materialize(); // there will be always at least one pass through the full index vector, typically at least two
            } else {
                index = indexArg;
            }
            int isize = index.size();
            boolean[] omit = null;
            int nomit = 0;

            for (int i = 0; i < isize; i++) {
                int v = index.getInt(i);
                if (v == RInt.NA) {
                    hasNA = true;
                    continue;
                }
                if (v == 0) {
                    nzeros++;
                    continue;
                }
                if (v > 0) {
                    hasPositive = true;
                    continue;
                }
                // v < 0
                if (!hasNegative) {
                    hasNegative = true;
                    omit = new boolean[bsize];
                }
                int vi = -v - 1;
                if (vi < omit.length) {
                    if (!omit[vi]) {
                        omit[vi] = true;
                        nomit++;
                    }
                }
            }
            boolean hasZero = nzeros > 0;

            if (!hasNegative) {
                if (!hasZero && symbols == null && !hasNA) { return base.subset(index); }
                // positive and zero indexes (and perhaps NAs)
                int nsize = isize - nzeros;
                if (symbols != null) {
                    newSymbols = new RSymbol[nsize];
                }
                RArray res = Utils.createArray(base, nsize, symbols != null);
                int j = 0;
                for (int i = 0; i < isize; i++) {
                    int v = index.getInt(i);
                    if (v > 0 && v <= bsize) { // note: RInt.NA < 0
                        res.set(j, base.get(v - 1));
                        if (symbols != null) {
                            newSymbols[j] = symbols[v - 1];
                        }
                        j++;
                        continue;
                    }
                    if (v == 0) {
                        continue;
                    }
                    Utils.setNA(res, j);
                    if (symbols != null) {
                        newSymbols[j] = RSymbol.NA_SYMBOL;
                    }
                    j++;
                }
                if (symbols != null) {
                    res = res.setNames(Names.create(newSymbols));
                }
                return res;
            } else { // hasNegative == true
                if (hasPositive || hasNA) { throw RError.getOnlyZeroMixed(ast); }
                // negative and zero indexes
                int nsize = bsize - nomit;
                RArray res = Utils.createArray(base, nsize, symbols != null);
                if (symbols != null) {
                    newSymbols = new RSymbol[nsize];
                }
                int j = 0;
                for (int i = 0; i < bsize; i++) {
                    if (!omit[i]) {
                        res.set(j, base.get(i));
                        if (symbols != null) {
                            newSymbols[j] = symbols[i];
                        }
                        j++;
                    }
                }
                if (symbols != null) {
                    res = res.setNames(Names.create(newSymbols));
                }
                return res;
            }
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing IntSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                RInt iindex;
                if (index instanceof RInt) {
                    iindex = (RInt) index;
                } else if (index instanceof RDouble) {
                    iindex = index.asInt();
                } else {
                    throw new SpecializationException(Failure.NOT_INT_OR_DOUBLE_INDEX);
                }
                return executeIntVector(iindex, abase, ast);

            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - IntSelection failed: " + f);
                GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                replace(gs, "install GenericSelection from IntSelection");
                if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                return gs.execute(index, base);
            }
        }
    }

    // for selections like d[x == c], where c is a scalar double constant and x is a double and d is a double ; and where d has the same size as x
    // FIXME: make this more generic, supporting any type of base "d" should not be (much?) slower
    public static class LogicalEqualitySelection extends BaseR {
        @Child RNode lhs;
        @Child RNode xExpr;
        final double c;

        public LogicalEqualitySelection(ASTNode ast, RNode lhs, RNode xExpr, double c) {
            // only for subset
            super(ast);
            this.lhs = adoptChild(lhs);
            this.xExpr = adoptChild(xExpr);
            assert Utils.check(RDouble.RDoubleUtils.isFinite(c));
            this.c = c;
        }

        @Override public Object execute(Frame frame) {
            assert Utils.check(getNewNode() == null);
            RAny base = (RAny) lhs.execute(frame); // note: order is important
            if (getNewNode() != null) {
                return ((ReadVector.LogicalEqualitySelection) getNewNode()).executeWithBase(frame, base);
            }
            return executeWithBase(frame,base);
        }

        public Object executeWithBase(Frame frame, RAny base) {
            RAny x = (RAny) xExpr.execute(frame);
            if (getNewNode() != null) {
                return ((ReadVector.LogicalEqualitySelection) getNewNode()).executeWithBase(frame, base);
            }
            return execute(base, x);
        }

        @Override
        protected <N extends RNode> N replaceChild(RNode oldNode, N newNode) {
            assert oldNode != null;
            if (lhs == oldNode) {
                lhs = newNode;
                return adoptInternal(newNode);
            }
            if (xExpr == oldNode) {
                xExpr = newNode;
                return adoptInternal(newNode);
            }
            return super.replaceChild(oldNode, newNode);
        }

        public RAny execute(RAny base, RAny xArg) {
            try {
                if (!(base instanceof DoubleImpl && xArg instanceof DoubleImpl)) { // FIXME: also could materialize
                    throw new SpecializationException(null);
                }
                // NOTE: in this case, it is very important for performance to access the arrays directly (found out experimentally)
                // it is indeed puzzling as e.g. with matrix multiply, going through .getDouble did cost nothing
                DoubleImpl bdi = (DoubleImpl) base;
                double[] b = ((DoubleImpl) base).getContent();
                double[] x = ((DoubleImpl) xArg).getContent();
                int size = b.length;
                if (x.length != size || bdi.names() != null) { throw new SpecializationException(null); }
                if (size > 1000) { // TODO: a tuning parameter, what is the right cutoff???
                    // NOTE: this is faster at least for larger vectors, I suppose because it is more cache friendly
                    // TODO: similar optimizations will likely help elsewhere
                    // TODO: this is yet another case for "growable" vector types, perhaps even non-contiguous
                    // TODO: this wastes memory, too
                    double[] tmp = new double[size];
                    int j = 0;
                    for (int i = 0; i < size; i++) {
                        double d = x[i];
                        if (d == c) {
                            tmp[j++] = b[i];
                        } else if (RDouble.RDoubleUtils.isNAorNaN(d)) {
                            tmp[j++] = RDouble.NA;
                        }
                    }
                    if (j == size) {
                        return RDouble.RDoubleFactory.getFor(tmp); // we are lucky
                    } else {
                        double[] content = new double[j];
                        System.arraycopy(tmp, 0, content, 0, j);
                        return RDouble.RDoubleFactory.getFor(content);
                    }

                } else {
                    int nsize = 0;
                    for (int i = 0; i < size; i++) {
                        double d = x[i];
                        if (d == c || RDouble.RDoubleUtils.isNAorNaN(d)) {
                            nsize++;
                        }
                    }
                    double[] content = new double[nsize];
                    int j = 0;
                    for (int i = 0; i < size; i++) {
                        double d = x[i];
                        if (d == c) {
                            content[j++] = b[i];
                        } else if (RDouble.RDoubleUtils.isNAorNaN(d)) {
                            content[j++] = RDouble.NA;
                        }
                    }
                    return RDouble.RDoubleFactory.getFor(content);
                }

            } catch (SpecializationException e) {
                AccessVector av = (AccessVector) ast;
                EQ eq = (EQ) av.getArgs().first().getValue();
                RDouble boxedC = RDouble.RDoubleFactory.getScalar(c);

                Comparison indexExpr = new Comparison(eq, xExpr, new Constant(eq.getRHS(), boxedC), r.nodes.exec.Comparison.getEQ());

                LogicalSelection ls = new LogicalSelection(ast, lhs, new RNode[]{indexExpr}, true);
                replace(ls, "install LogicalSelection from LogicalEqualitySelection");
                if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with LogicalSelection");

                // index
                return ls.execute((RAny) indexExpr.execute(xArg, boxedC), base);

            }
        }
    }

    // when the index is a logical vector of the same length as the base
    //   rewrites itself for other cases
    public static class SimpleLogicalSelection extends ReadVector {
        public SimpleLogicalSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(subset);
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing SimpleLogicalSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (!(index instanceof RLogical)) { throw new SpecializationException(Failure.NOT_LOGICAL_INDEX); }
                if (abase.names() != null) { throw new SpecializationException(Failure.BASE_HAS_NAMES); }
                RLogical lindex = (RLogical) index;
                int isize = lindex.size();
                int bsize = abase.size();
                if (isize != bsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                int nsize = 0;
                for (int i = 0; i < isize; i++) {
                    if (lindex.getLogical(i) != RLogical.FALSE) {
                        nsize++;
                    }
                }
                RArray res = Utils.createArray(base, nsize);
                int j = 0;
                for (int i = 0; i < isize; i++) {
                    int l = lindex.getLogical(i);
                    if (l == RLogical.TRUE) {
                        res.set(j++, abase.get(i));
                    } else if (l == RLogical.NA) {
                        Utils.setNA(res, j++);
                    }
                }
                return res;
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - SimpleLogicalSelection failed: " + f);
                switch (f) {
                case NOT_SAME_LENGTH:
                case BASE_HAS_NAMES:
                    LogicalSelection ls = new LogicalSelection(ast, lhs, indexes, subset);
                    replace(ls, "install LogicalSelection from SimpleLogicalSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with LogicalSelection");
                    return ls.execute(index, base);

                default:
                    GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                    replace(gs, "install GenericSelection from SimpleLogicalSelection");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                    return gs.execute(index, base);
                }
            }
        }
    }

    // when the index is a logical vector
    //   rewrites itself for other cases
    public static class LogicalSelection extends ReadVector {
        public LogicalSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(subset);
        }

        public static RAny executeLogicalVector(RLogical index, RArray base) {
            int isize = index.size();
            int bsize = base.size();
            Names names = base.names();
            if (isize == 0) { return Utils.createEmptyArray(base, names != null); }
            RSymbol[] symbols = (names == null) ? null : names.sequence();
            RSymbol[] newSymbols = null;

            if (isize >= bsize) {
                // no re-use of index, but index can be longer than base
                int nsize = 0;
                for (int i = 0; i < isize; i++) {
                    if (index.getLogical(i) != RLogical.FALSE) {
                        nsize++;
                    }
                }
                RArray res = Utils.createArray(base, nsize, symbols != null);
                if (symbols != null) {
                    newSymbols = new RSymbol[nsize];
                }
                int j = 0;
                int i = 0;
                for (; i < bsize; i++) {
                    int l = index.getLogical(i);
                    if (l == RLogical.TRUE) {
                        res.set(j, base.get(i));
                        if (symbols != null) {
                            newSymbols[j] = symbols[i];
                        }
                        j++;
                    } else if (l == RLogical.NA) {
                        Utils.setNA(res, j);
                        if (symbols != null) {
                            newSymbols[j] = RSymbol.NA_SYMBOL;
                        }
                        j++;
                    }
                }
                for (; i < isize; i++) {
                    int l = index.getLogical(i);
                    if (l != RLogical.FALSE) {
                        Utils.setNA(res, j);
                        if (symbols != null) {
                            newSymbols[j] = RSymbol.NA_SYMBOL;
                        }
                        j++;
                    }
                }
                if (symbols != null) {
                    res = res.setNames(Names.create(newSymbols));
                }
                return res;
            } else {
                // index is re-used and is shorter than base
                int nsize = 0;
                int j = 0;
                for (int i = 0; i < bsize; i++) {
                    if (index.getLogical(j) != RLogical.FALSE) {
                        nsize++;
                    }
                    j++;
                    if (j == isize) {
                        j = 0;
                    }
                }
                RArray res = Utils.createArray(base, nsize, symbols != null);
                if (symbols != null) {
                    newSymbols = new RSymbol[nsize];
                }
                j = 0;
                int k = 0;
                for (int i = 0; i < bsize; i++) {
                    int l = index.getLogical(j);
                    if (l == RLogical.TRUE) {
                        res.set(k, base.get(i));
                        if (symbols != null) {
                            newSymbols[k] = symbols[i];
                        }
                        k++;
                    } else if (l == RLogical.NA) {
                        Utils.setNA(res, k);
                        if (symbols != null) {
                            newSymbols[k] = RSymbol.NA_SYMBOL;
                        }
                        k++;
                    }
                    j++;
                    if (j == isize) {
                        j = 0;
                    }
                }
                if (symbols != null) {
                    res = res.setNames(Names.create(newSymbols));
                }
                return res;
            }
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing LogicalSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                if (!(index instanceof RLogical)) { throw new SpecializationException(Failure.NOT_LOGICAL_INDEX); }
                return executeLogicalVector((RLogical) index, (RArray) base);

            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - LogicalSelection failed: " + f);
                GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                replace(gs, "install GenericSelection from LogicalSelection");
                if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                return gs.execute(index, base);
            }
        }
    }

    // when the index is a vector of strings (selection by name)
    //   rewrites itself for other cases
    public static class StringSelection extends ReadVector {
        public StringSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(subset);
        }

        public static RAny executeStringVector(RString index, RArray base) {
            int isize = index.size();
            if (isize == 0) { return Utils.createEmptyArray(base, base.names() != null); }
            Names baseNames = base.names();
            if (baseNames == null) { return Utils.createNAArray(base, isize); }
            RSymbol[] symbols = new RSymbol[isize];
            RArray res = Utils.createArray(base, isize, true);
            for (int i = 0; i < isize; i++) {
                RSymbol symbol = RSymbol.getSymbol(index.getString(i));
                int v = baseNames.map(symbol);
                if (v != -1) {
                    res.set(i, base.get(v));
                    symbols[i] = symbol;
                } else {
                    Utils.setNA(res, i);
                    symbols[i] = RSymbol.NA_SYMBOL;
                }
            }
            return res.setNames(Names.create(symbols));
        }

        // lazy helps when not all elements are needed and when the names are not needed
        public static class RStringSubset extends View.RStringView implements RString {

            final RString base;
            final RString index;
            final Names baseNames;
            RString result; // a caching view

            public RStringSubset(RString base, RString index, Names baseNames) {
                this.base = base;
                this.index = index;
                this.baseNames = baseNames;
                assert Utils.check(baseNames != null);
            }

            public int size() {
                return index.size();
            }

            public String getString(int i) {
                if (result != null) {  // using the cache
                    return result.getString(i);
                }
                RSymbol symbol = RSymbol.getSymbol(index.getString(i));
                int v = baseNames.map(symbol);
                if (v != -1) { // FIXME: not caching individual accesses
                    return base.getString(v);
                } else {
                    return RString.NA;
                }
            }

            @Override
            public boolean isSharedReal() {
                return base.isShared() || index.isShared();
            }

            @Override
            public void ref() {
                base.ref();
                index.ref();
            }

            @Override
            public boolean dependsOn(RAny v) {
                return base.dependsOn(v) || index.dependsOn(v);
            }

            @Override
            public Names names() {
                return materialize().names();
            }

            @Override
            public RString materialize() {
                if (result == null) {
                    result = (RString) executeStringVector(index, base);
                }
                return result;
            }

            @Override
            public void visit_all(ValueVisitor v) {
                index.accept(v);
                base.accept(v);
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }

        }

        public static class RDoubleSubset extends View.RDoubleView implements RDouble {

            final RDouble base;
            final RString index;
            final Names baseNames;
            RDouble result; // a caching view

            public RDoubleSubset(RDouble base, RString index, Names baseNames) {
                this.base = base;
                this.index = index;
                this.baseNames = baseNames;
                assert Utils.check(baseNames != null);
            }

            public int size() {
                return index.size();
            }

            public double getDouble(int i) {
                if (result != null) {  // using the cache
                    return result.getDouble(i);
                }
                RSymbol symbol = RSymbol.getSymbol(index.getString(i));
                int v = baseNames.map(symbol);
                if (v != -1) { // FIXME: not caching individual accesses
                    return base.getDouble(v);
                } else {
                    return RDouble.NA;
                }
            }

            @Override
            public boolean isSharedReal() {
                return base.isShared() || index.isShared();
            }

            @Override
            public void ref() {
                base.ref();
                index.ref();
            }

            @Override
            public boolean dependsOn(RAny v) {
                return base.dependsOn(v) || index.dependsOn(v);
            }

            @Override
            public Names names() {
                return materialize().names();
            }

            @Override
            public RDouble materialize() {
                if (result == null) {
                    result = (RDouble) executeStringVector(index, base);
                }
                return result;
            }

            @Override
            public void visit_all(ValueVisitor v) {
                index.accept(v);
                base.accept(v);
            }

            @Override
            public void accept(ValueVisitor v) {
                v.visit(this);
            }

        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing StringSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (!(index instanceof RString)) { throw new SpecializationException(Failure.NOT_STRING_INDEX); }
                RString sindex = (RString) index;
                Names baseNames = abase.names();
                if (baseNames != null) {
                    if (abase instanceof RString) {
                        return TracingView.ViewTrace.trace(new RStringSubset((RString) abase, sindex, baseNames));
                    }
                    if (abase instanceof RDouble) {
                        return TracingView.ViewTrace.trace(new RDoubleSubset((RDouble) abase, sindex, baseNames));
                    }
                    // TODO: add more specializations
                }
                return executeStringVector(sindex, abase);

            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - StringSelection failed: " + f);
                GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset);
                replace(gs, "install GenericSelection from StringSelection");
                if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                return gs.execute(index, base);
            }
        }
    }

    // when the index is a vector of integers (selection by index)
    //   and the base can be recursive
    //   and the mode is subscript ([[.]])
    public static class Subscript extends ReadVector {
        public Subscript(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
            Utils.check(!subset);
        }

        public static int convertNegativeNonNAIndex(int indexv, int bsize, ASTNode ast) {
            int res;

            if (bsize > 2) { throw RError.getSelectMoreThanOne(ast); }
            if (bsize < 2) { throw RError.getSelectLessThanOne(ast); }
            // bsize == 2
            if (indexv == -1) {
                res = 1;
            } else if (indexv == -2) {
                res = 0;
            } else if (indexv == 0) {
                throw RError.getSelectLessThanOne(ast);
            } else {
                throw RError.getSelectMoreThanOne(ast);
            }
            return res;
        }

        public static int convertDereferencingIndex(int indexv, int iindex, int bsize, ASTNode ast) {
            int isel;
            if (indexv > 0) {
                if (indexv <= bsize) { // NOTE: RInt.NA < 0
                    isel = indexv - 1;
                } else {
                    throw RError.getNoSuchIndexAtLevel(ast, iindex + 1);
                }
            } else {
                if (indexv == RInt.NA) { throw RError.getNoSuchIndexAtLevel(ast, iindex + 1); }
                isel = convertNegativeNonNAIndex(indexv, bsize, ast);
            }
            return isel;
        }

        // note: subscript does not preserve names in the base, so no name handling (of base) is needed here
        public static RAny executeSubscript(RInt index, RArray base, ASTNode ast) {
            final int isize = index.size();
            if (isize == 0) { throw RError.getSelectLessThanOne(ast); }
            int i = 0;
            RAny b = base;

            if (isize > 1) {
                // the upper levels of recursive indexes have to be treated differently from the lowest level (the error semantics is different)
                // also, we know that the upper levels must be lists
                for (; i < isize - 1; i++) {
                    if (!(b instanceof RList)) {
                        if (base instanceof RList) {
                            throw RError.getRecursiveIndexingFailed(ast, i + 1);
                        } else {
                            throw RError.getSelectMoreThanOne(ast);
                        }
                    }
                    RList l = (RList) b;
                    int indexv = index.getInt(i);
                    int bsize = l.size();
                    int isel = convertDereferencingIndex(indexv, i, bsize, ast);
                    b = l.getRAny(isel);
                }
            }
            // selection at the last level
            int indexv = index.getInt(i);
            if (!(b instanceof RArray)) {
                // TODO: support language objects
                if (indexv == 1) { throw RError.getInvalidTypeLength(ast, b.typeOf(), 1); // FIXME: a very obscure error message but what GNU-R returns
                }
                if (indexv > 1) { throw RError.getSubscriptBounds(ast); }
                throw RError.getSelectLessThanOne(ast);
            }
            RArray a = (RArray) b;
            int bsize = a.size();
            boolean isList = a instanceof RList;

            if (indexv > 0) {
                if (indexv <= bsize) {
                    if (isList) {
                        return ((RList) a).getRAny(indexv - 1);
                    } else {
                        return a.boxedGet(indexv - 1);
                    }
                } else {
                    throw RError.getSubscriptBounds(ast);
                }
            } else {
                if (indexv == RInt.NA) {
                    if (isList) {
                        return RList.NULL;
                    } else {
                        throw RError.getSubscriptBounds(ast);
                    }
                }
                int fromIndex = convertNegativeNonNAIndex(indexv, bsize, ast);
                if (isList) {
                    return ((RList) a).getRAny(fromIndex);
                } else {
                    return a.boxedGet(fromIndex);
                }
            }
        }

        public static RAny executeSubscript(RString index, RArray base, ASTNode ast) {
            final int isize = index.size();
            if (isize == 0) { throw RError.getSelectLessThanOne(ast); }
            int i = 0;
            RAny b = base;

            if (isize > 1) {
                // the upper levels of recursive indexes have to be treated differently from the lowest level (the error semantics is different)
                // also, we know that the upper levels must be lists
                for (; i < isize - 1; i++) {
                    if (!(b instanceof RList)) { throw RError.getSelectMoreThanOne(ast); }
                    RList l = (RList) b;
                    Names names = l.names();
                    if (names == null) { throw RError.getNoSuchIndexAtLevel(ast, i + 1); }
                    RSymbol s = RSymbol.getSymbol(index.getString(i));
                    int indexv = names.map(s);
                    if (indexv == -1) { throw RError.getNoSuchIndexAtLevel(ast, i + 1); }
                    b = l.getRAny(indexv);
                }
            }
            // selection at the last level
            if (!(b instanceof RArray)) { throw RError.getSubscriptBounds(ast); // NOTE: this makes more sense than the error message with integer index
            // (both are to mimic GNU-R)
            }
            RArray a = (RArray) b;
            Names names = a.names();
            int indexv = -1;
            if (names != null) {
                RSymbol s = RSymbol.getSymbol(index.getString(i));
                indexv = names.map(s);
            }
            boolean isList = a instanceof RList;
            if (indexv != -1) {
                if (isList) {
                    return ((RList) a).getRAny(indexv);
                } else {
                    return a.boxedGet(indexv);
                }
            } else {
                if (isList) {
                    return RList.NULL;
                } else {
                    throw RError.getSubscriptBounds(ast);
                }
            }
        }

        public static RAny executeSubscript(RAny index, RArray base, ASTNode ast) {
            if (index instanceof RInt || index instanceof RDouble || index instanceof RLogical) { return executeSubscript(index.asInt(), base, ast); }
            if (index instanceof RString) { return executeSubscript((RString) index, base, ast); }
            throw invalidSubscript(index, ast);
        }

        public static RError invalidSubscript(RAny index, ASTNode ast) {
            if (index instanceof RList) {
                int lsize = ((RList) index).size();
                if (lsize == 1) { throw RError.getInvalidSubscriptType(ast, index.typeOf()); }
                if (lsize == 0) { throw RError.getSelectLessThanOne(ast); }
                throw RError.getSelectMoreThanOne(ast);
            }
            if (index instanceof RNull) { throw RError.getSelectLessThanOne(ast); }
            throw RError.getInvalidSubscriptType(ast, index.typeOf());
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing Subscript");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                return executeSubscript(index, abase, ast);
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_SEL) Utils.debug("selection - Subscript failed: " + f);
                GenericSelection gs = new GenericSelection(ast, lhs, indexes, subset); // rewriting itself only to handle the error, there is no way to recover
                replace(gs, "install GenericSelection from Subscript");
                if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                return gs.execute(index, base);
            }
        }
    }

    // any selection, won't rewrite itself
    public static class GenericSelection extends ReadVector {
        public GenericSelection(ASTNode ast, RNode lhs, RNode[] indexes, boolean subset) {
            super(ast, lhs, indexes, subset);
        }

        @Override public RAny execute(RAny index, RAny base) {
            if (DEBUG_SEL) Utils.debug("selection - executing GenericSelection");
            if (!(base instanceof RArray)) { throw RError.getObjectNotSubsettable(ast, base.typeOf()); }
            assert Utils.check(subset);
            RArray abase = (RArray) base;
            if (!(index instanceof RArray) || index instanceof RList) { throw RError.getInvalidSubscriptType(ast, index.typeOf()); }
            RArray aindex = (RArray) index;
            int isize = aindex.size();
            if (isize == 1) { return GenericScalarSelection.executeScalar(abase, aindex, subset, ast); }
            if (aindex instanceof RInt) {
                return IntSelection.executeIntVector((RInt) aindex, abase, ast);
            } else if (aindex instanceof RDouble) {
                return IntSelection.executeIntVector(aindex.asInt(), abase, ast);
            } else if (aindex instanceof RLogical) {
                return LogicalSelection.executeLogicalVector((RLogical) aindex, abase);
            } else if (aindex instanceof RString) {
                return StringSelection.executeStringVector((RString) aindex, abase);
            } else if (aindex instanceof RNull) { return Utils.createEmptyArray(abase); }
            throw RError.getInvalidSubscriptType(ast, aindex.typeOf());
        }
    }

    /**
     * Read access to a list using the dollar selector. Works only on lists, fails otherwise (compatible with R >= 2.6).
     * Because only string literals and symbols are allowed, the symbol creation and lookup is precached and the field
     * access only does the hashmap search in the names property of the list. If the list is empty, null element is
     * returned, as if when the desired field is not present.
     */
    public abstract static class FieldSelection extends BaseR {

        @Child RNode lhs;
        final RSymbol index;

        protected FieldSelection(ASTNode orig, RNode lhs, RSymbol index) {
            super(orig);
            this.lhs = adoptChild(lhs);
            this.index = index;
        }

        @Override public Object execute(Frame frame) {
            assert Utils.check(getNewNode() == null);
            RAny base = (RAny) lhs.execute(frame);
            if (getNewNode() != null) {
                return ((ReadVector.FieldSelection)getNewNode()).execute(base);
            }
            return execute(base);
        }

        @Override
        protected <N extends RNode> N replaceChild(RNode oldNode, N newNode) {
            assert oldNode != null;
            if (lhs == oldNode) {
                lhs = newNode;
                return adoptInternal(newNode);
            }
            return super.replaceChild(oldNode, newNode);
        }

        /** As per R reference, should fail if not list or pairlist, otherwise the element should be returned. */
        abstract RAny execute(RAny base);

        public static class UninitializedSelection extends FieldSelection {

            public UninitializedSelection(ASTNode parent, RNode lhs, RSymbol index) {
                super(parent, lhs, index);
            }

            @Override RAny execute(RAny base) {
                try {
                    throw new SpecializationException(null);
                } catch (SpecializationException e) {
                    if (base instanceof RList) {
                        RList list = (RList) base;
                        RArray.Names names = list.names();

                        if (names != null) {
                            int pos = names.map(index);
                            if (pos == -1) {
                                pos = names.mapPartial(index);
                            }
                            if (pos != -1) {
                                FixedPositionSelection fp = new FixedPositionSelection(ast, lhs, index, pos, names);
                                replace(fp, "install FixedPositionSelection from UninitializedSelection (Field)");
                                if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with FixedPositionSelection");
                                return fp.execute(base);
                            }
                        }
                    }
                    GenericSelection gs = new GenericSelection(ast, lhs, index);
                    replace(gs, "install GenericSelection from SimpleSelection (Field)");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                    return gs.execute(base);
                }
            }
        }

        public static class FixedPositionSelection extends FieldSelection {

            final int fixedPosition;
            final Names fixedNames;

            public FixedPositionSelection(ASTNode parent, RNode lhs, RSymbol index, int fixedPosition, Names fixedNames) {
                super(parent, lhs, index);
                this.fixedPosition = fixedPosition;
                this.fixedNames = fixedNames;
            }

            @Override RAny execute(RAny base) {
                try {
                    if (base instanceof RList) {
                        RList list = (RList) base;
                        RArray.Names names = list.names();
                        if (names == fixedNames) { return list.getRAny(fixedPosition); }
                    }
                    throw new SpecializationException(null);
                } catch (SpecializationException e) {
                    GenericSelection gs = new GenericSelection(ast, lhs, index);
                    replace(gs, "install GenericSelection from FixedPositionSelection (Field)");
                    if (DEBUG_SEL) Utils.debug("selection - replaced and re-executing with GenericSelection");
                    return gs.execute(base);
                }
            }
        }

        public static class GenericSelection extends FieldSelection {

            int lastPosition = -1;
            Names lastNames = null;

            public GenericSelection(ASTNode parent, RNode lhs, RSymbol index) {
                super(parent, lhs, index);
            }

            @Override RAny execute(RAny base) {
                if (!(base instanceof RList)) { throw RError.getDollarAtomicVectors(ast); }
                RList list = (RList) base;
                RArray.Names names = list.names();
                int pos;
                if (names != lastNames) {
                    pos = names.map(index);
                    if (pos == -1) {
                        pos = names.mapPartial(index);
                    }
                    lastPosition = pos;
                    lastNames = names;
                } else {
                    pos = lastPosition;
                }
                if (pos == -1) { return RNull.getNull(); }
                return list.getRAny(pos); // list subscript does not preserve names
            }
        }
    }
}
TOP

Related Classes of r.nodes.exec.ReadVector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.