Package r.nodes.exec

Source Code of r.nodes.exec.UpdateVector$DoubleBaseSimpleSelection$ScalarIntSelection

package r.nodes.exec;

import r.*;
import r.data.*;
import r.data.RArray.Names;
import r.data.internal.*;
import r.errors.RError;
import r.nodes.ast.*;
import r.runtime.*;

import java.util.*;

// TODO: clean-up generic code using .getRef

// FIXME: the code handling the replacement of a variable could be replaced via
// ReplacementCall

// FIXME: could reduce code size by some refactoring, e.g. subclassing on
// copiers that use double, int, logical
// FIXME: some of the guards from common "exec" methods could be elided - type
// checking for RArray and then again in specialized functions for RArray
// subclasses

public abstract class UpdateVector extends BaseR {

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

    @Child RNode assign; // node which will assign the whole new vector to var
    RAny newVector;
    final boolean isSuper;

    int frameSlot = -1; // FIXME: a lot of cached data, should split into nodes if possible
    boolean slotInitialized = false;

    private static final boolean DEBUG_UP = false;

    protected UpdateVector(UpdateVector from) {
        this(from.ast, from.isSuper, from.var, from.lhs, from.indexes, from.rhs, from.subset);
    }

    UpdateVector(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
        super(ast);
        this.var = var;
        this.lhs = adoptChild(lhs); // lhs is always SimpleAccessVariable of var
        this.indexes = adoptChildren(indexes);
        this.rhs = adoptChild(rhs);
        this.subset = subset;
        this.isSuper = isSuper;

        if (isSuper) { // FIXME: turn this switch into node rewriting?
            RNode node = new BaseR(ast) {
                @Override public final Object execute(Frame frame) {
                    return newVector;
                }
            };
            this.assign = adoptChild(SuperWriteVariable.getUninitialized(ast, var, node));
        } else {
            // this.assign = updateParent(WriteVariable.getUninitialized(ast, var, node));
            this.assign = null;
        }
    }

    enum Failure {
        NOT_ARRAY_BASE,
        NOT_ONE_ELEMENT_INDEX,
        NOT_NUMERIC_INDEX,
        NOT_LOGICAL_INDEX,
        NOT_ARRAY_INDEX,
        NOT_INT_SEQUENCE_INDEX,
        INDEX_OUT_OF_BOUNDS,
        NOT_ONE_ELEMENT_VALUE,
        NOT_ARRAY_VALUE,
        UNEXPECTED_TYPE,
        NOT_SAME_LENGTH,
        MAYBE_VECTOR_UPDATE,
        UNEXPECTED_DEPENDENCY,
        SHARED_BASE
    }

    public final Object executeSuper(Frame frame) {
        RAny value = (RAny) rhs.execute(frame); // note: order is important
        RAny index = (RAny) indexes[0].execute(frame);

        RAny base;
        if (frame != null) { // FIXME: turn this guard into node rewriting, it only has to be done once
            base = (RAny) lhs.execute(frame.enclosingFrame());
        } else {
            throw RError.getUnknownVariable(ast, var);
        }
        // NOTE: we don't ref here
        newVector = execute(base, index, value);
        assign.execute(frame); // FIXME: may ref unnecessarily
        return value;
    }

    @Override public final Object execute(Frame frame) {
        assert Utils.check(getNewNode() == null);
        if (isSuper) { return executeSuper(frame); }
        RAny value = (RAny) rhs.execute(frame); // note: order is important
        assert Utils.check(getNewNode() == null); // FIXME
        RAny index = (RAny) indexes[0].execute(frame);
        assert Utils.check(getNewNode() == null); // FIXME

        if (frame != null) {
            if (!slotInitialized) { // FIXME: turn this into node rewriting
                frameSlot = frame.findVariable(var);
                slotInitialized = true;
            }
            // variable has a local slot
            // note: except for dynamic invocation, this always has to be the case because the variable is in the write set
            if (frameSlot != -1) {
                RAny base = Utils.cast(frame.getObjectForcingPromises(frameSlot));
                if (base != null) {
                    RAny newBase = execute(base, index, value);
                    if (newBase != base) {
                        frame.writeAtRef(frameSlot, newBase);
                    }
                } else { // this should be uncommon
                    base = Utils.cast(frame.readViaWriteSetSlowPath(var));
                    if (base == null) { throw RError.getUnknownVariable(getAST(), var); }
                    base.ref(); // reading from parent, hence need to copy on update
                    // ref once will make it shared unless it is stateless (like int sequence)
                    RAny newBase = execute(base, index, value);
                    // now typically base != newBase, but not always (an update may actually change nothing in the base vector)
                    frame.writeAtRef(frameSlot, newBase);
                }
            } else {
                // dynamic invocation
                // TODO: this is super-inefficient
                RAny base = Utils.cast(frame.read(var));
                if (base == null) { throw RError.getUnknownVariable(getAST(), var); }
                base.ref(); // TODO: this may ref unnecessarily, will copy every time invoked
                RAny newBase = execute(base, index, value);
                assert Utils.check(base != newBase);
                frame.writeToExtension(var, newBase);
            }
        } else {
            // variable is top-level
            RAny base = Utils.cast(var.getValue());
            if (base == null) { throw RError.getUnknownVariable(getAST(), var); }
            RAny newBase = execute(base, index, value);
            if (newBase != base) {
                Frame.writeToTopLevelRef(var, newBase);
            }
        }
        return value;
    }

    @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);
                }
            }
        }
        if (rhs == oldNode) {
            rhs = newNode;
            return adoptInternal(newNode);
        }
        if (assign == oldNode) {
            assign = newNode;
            return adoptInternal(newNode);
        }
        return super.replaceChild(oldNode, newNode);
    }

    abstract RAny execute(RAny base, RAny index, RAny value);

    // FIXME: move these to some other file?
    public static Names expandNames(Names names, int newSize) {
        RSymbol[] oldSymbols = names.sequence();
        RSymbol[] symbols = new RSymbol[newSize];
        System.arraycopy(oldSymbols, 0, symbols, 0, oldSymbols.length);
        Arrays.fill(symbols, oldSymbols.length, newSize, RSymbol.EMPTY_SYMBOL);
        return Names.create(symbols);
    }

    public static Names removeName(Names names, int removeIndex) {
        RSymbol[] oldSymbols = names.sequence();
        int nsize = oldSymbols.length - 1;
        RSymbol[] symbols = new RSymbol[nsize];
        System.arraycopy(oldSymbols, 0, symbols, 0, removeIndex);
        if (removeIndex < nsize) {
            System.arraycopy(oldSymbols, removeIndex + 1, symbols, removeIndex, nsize - removeIndex);
        }
        return Names.create(symbols);
    }

    public static Names appendName(Names names, RSymbol newName) {
        RSymbol[] oldSymbols = names.sequence();
        int size = oldSymbols.length;
        RSymbol[] symbols = new RSymbol[size + 1];
        System.arraycopy(oldSymbols, 0, symbols, 0, size);
        symbols[size] = newName;
        HashMap<RSymbol, Integer> oldMap = names.stealMap();
        if (oldMap != null && !oldMap.containsKey(newName)) {
            oldMap.put(newName, size);
        }
        return Names.create(symbols, oldMap);
    }

    // for an update of a materialized double private vector using a double scalar,
    // indexed by a scalar (only simple cases)
    public abstract static class DoubleBaseSimpleSelection extends UpdateVector {
        public DoubleBaseSimpleSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
        }

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

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                try {
                    if (!(index instanceof ScalarIntImpl)) { throw new SpecializationException(null); }
                    int i = (((ScalarIntImpl) index).getInt()) - 1;
                    if (!(base instanceof DoubleImpl)) { throw new SpecializationException(null); }
                    DoubleImpl dibase = (DoubleImpl) base;
                    if (dibase.isShared()) { throw new SpecializationException(null); }
                    double[] dbase = dibase.getContent();
                    if (i < 0 || i >= dbase.length) { throw new SpecializationException(null); }
                    if (!(value instanceof ScalarDoubleImpl)) { throw new SpecializationException(null); }
                    double dvalue = ((ScalarDoubleImpl) value).getDouble();

                    dbase[i] = dvalue;

                    return dibase;
                } catch (SpecializationException e) {
                    ScalarDoubleSelection ns = new ScalarDoubleSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                    replace(ns, "install DoubleBaseSimpleSelection.ScalarDoubleSelection from DoubleBaseSimpleSelection.ScalarIntSelection");
                    return ns.execute(base, index, value);
                }
            }
        }

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

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                try {
                    if (!(index instanceof ScalarDoubleImpl)) { throw new SpecializationException(null); }
                    int i = Convert.double2int(((ScalarDoubleImpl) index).getDouble()) - 1;

                    if (!(base instanceof DoubleImpl)) { // FIXME: extract to a static method? (without performance overhead)
                        throw new SpecializationException(null);
                    }
                    DoubleImpl dibase = (DoubleImpl) base;
                    if (dibase.isShared()) { throw new SpecializationException(null); }
                    double[] dbase = dibase.getContent();
                    if (i < 0 || i >= dbase.length) { throw new SpecializationException(null); }
                    if (!(value instanceof ScalarDoubleImpl)) { throw new SpecializationException(null); }
                    double dvalue = ((ScalarDoubleImpl) value).getDouble();

                    dbase[i] = dvalue;

                    return dibase;
                } catch (SpecializationException e) {
                    ScalarDoubleWithAttributesSelection ns = new ScalarDoubleWithAttributesSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                    replace(ns, "install ScalarDoubleWithAttributesSelection from DoubleBaseSimpleSelection.ScalarIntSelection");
                    return ns.execute(base, index, value);
                }
            }
        }

        public static class ScalarDoubleWithAttributesSelection extends DoubleBaseSimpleSelection {
            public ScalarDoubleWithAttributesSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
                super(ast, isSuper, var, lhs, indexes, rhs, subset);
            }

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                try {
                    if (!(index instanceof ScalarDoubleImpl)) { throw new SpecializationException(null); }
                    int i = Convert.double2int(((ScalarDoubleImpl) index).getDouble()) - 1;

                    if (!(base instanceof DoubleImpl)) { // FIXME: extract to a static method? (without performance overhead)
                        throw new SpecializationException(null);
                    }
                    DoubleImpl dibase = (DoubleImpl) base;
                    if (dibase.isShared()) { throw new SpecializationException(null); }
                    double[] dbase = dibase.getContent();
                    if (i < 0 || i >= dbase.length) { throw new SpecializationException(null); }
                    if (!(value instanceof RDouble)) { throw new SpecializationException(null); }
                    RDouble dblvalue = (RDouble) value;
                    if (dblvalue.size() != 1) { throw new SpecializationException(null); }
                    dbase[i] = dblvalue.getDouble(0);
                    return dibase;
                } catch (SpecializationException e) {
                    ScalarNumericSelection ns = new ScalarNumericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                    replace(ns, "install ScalarNumericSelection from DoubleBaseSimpleSelection.ScalarIntSelection");
                    return ns.execute(base, index, value);
                }
            }
        }
    }

    // for a numeric (int, double) scalar index, first installs an uninitialized node
    // this node rewrites itself to type-specialized nodes for simple assignment, or to a generic node
    // the specialized nodes can rewrite themselves to the generic node
    // rewrites to GenericScalarSelection when types change or otherwise needed
    public static class ScalarNumericSelection extends UpdateVector {

        public ScalarNumericSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing ScalarNumericSelection (uninitialized)");

            try {
                throw new SpecializationException(null);
            } catch (SpecializationException e) {
                Specialized sn = createSimple(base, value);
                if (sn != null) {
                    replace(sn, "specialize ScalarNumericSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with ScalarNumericSelection.Simple");
                    return sn.execute(base, index, value);
                } else {
                    sn = createGeneric();
                    replace(sn, "specialize ScalarNumericSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with ScalarNumericSelection.Generic");
                    return sn.execute(base, index, value);
                }
            }
        }

        abstract class ValueCopy {
            abstract RAny copy(RArray base, int pos, RAny value) throws SpecializationException;
        }

        public Specialized createSimple(RAny baseTemplate, RAny valueTemplate) {
            if (baseTemplate instanceof RInt) {
                if (valueTemplate instanceof ScalarIntImpl) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                            if (!(base instanceof RInt) || !(value instanceof ScalarIntImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RInt ibase = (RInt) base;
                            int bsize = ibase.size();
                            if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int zpos = pos - 1;
                            if (!ibase.isShared()) {
                                return ibase.set(zpos, ((ScalarIntImpl) value).getInt());
                            } else {
                                int[] content = new int[bsize];
                                int i = 0;
                                for (; i < zpos; i++) {
                                    content[i] = ibase.getInt(i);
                                }
                                content[i++] = ((ScalarIntImpl) value).getInt();
                                for (; i < bsize; i++) {
                                    content[i] = ibase.getInt(i);
                                }
                                return RInt.RIntFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RInt,ScalarInt>");
                }
                if (valueTemplate instanceof ScalarLogicalImpl) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                            if (!(base instanceof RInt) || !(value instanceof ScalarLogicalImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RInt ibase = (RInt) base;
                            int bsize = ibase.size();
                            if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int zpos = pos - 1;
                            if (!ibase.isShared()) {
                                return ibase.set(zpos, ((ScalarLogicalImpl) value).getLogical());
                            } else {
                                int[] content = new int[bsize];
                                int i = 0;
                                for (; i < zpos; i++) {
                                    content[i] = ibase.getInt(i);
                                }
                                content[i++] = ((ScalarLogicalImpl) value).getLogical();
                                for (; i < bsize; i++) {
                                    content[i] = ibase.getInt(i);
                                }
                                return RInt.RIntFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RInt,ScalarLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RDouble) {
                if (valueTemplate instanceof ScalarDoubleImpl) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                            if (!(base instanceof RDouble) || !(value instanceof ScalarDoubleImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RDouble dbase = (RDouble) base;
                            int bsize = dbase.size();
                            if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int zpos = pos - 1;
                            if (!dbase.isShared()) {
                                return dbase.set(zpos, ((ScalarDoubleImpl) value).getDouble());
                            } else {
                                double[] content = new double[bsize];
                                int i = 0;
                                for (; i < zpos; i++) {
                                    content[i] = dbase.getDouble(i);
                                }
                                content[i++] = ((ScalarDoubleImpl) value).getDouble();
                                for (; i < bsize; i++) {
                                    content[i] = dbase.getDouble(i);
                                }
                                return RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RDouble,ScalarDouble>");
                }
                if (valueTemplate instanceof ScalarIntImpl) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                            if (!(base instanceof RDouble) || !(value instanceof ScalarIntImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RDouble dbase = (RDouble) base;
                            int bsize = dbase.size();
                            if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int zpos = pos - 1;
                            if (!dbase.isShared()) {
                                return dbase.set(zpos, Convert.int2double(((ScalarIntImpl) value).getInt()));
                            } else {
                                double[] content = new double[bsize];
                                int i = 0;
                                for (; i < zpos; i++) {
                                    content[i] = dbase.getDouble(i);
                                }
                                content[i++] = Convert.int2double(((ScalarIntImpl) value).getInt());
                                for (; i < bsize; i++) {
                                    content[i] = dbase.getDouble(i);
                                }
                                return RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RDouble,ScalarInt>");
                }
                if (valueTemplate instanceof ScalarLogicalImpl) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                            if (!(base instanceof RDouble) || !(value instanceof ScalarLogicalImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RDouble dbase = (RDouble) base;
                            int bsize = dbase.size();
                            if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int zpos = pos - 1;
                            if (!dbase.isShared()) {
                                return dbase.set(zpos, Convert.logical2double(((ScalarLogicalImpl) value).getLogical()));
                            } else {
                                double[] content = new double[bsize];
                                int i = 0;
                                for (; i < zpos; i++) {
                                    content[i] = dbase.getDouble(i);
                                }
                                content[i++] = Convert.logical2double(((ScalarLogicalImpl) value).getLogical());
                                for (; i < bsize; i++) {
                                    content[i] = dbase.getDouble(i);
                                }
                                return RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RDouble,ScalarLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RLogical) {
                if (valueTemplate instanceof ScalarLogicalImpl) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                            if (!(base instanceof RLogical) || !(value instanceof ScalarLogicalImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RLogical lbase = (RLogical) base;
                            int bsize = lbase.size();
                            if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int zpos = pos - 1;
                            if (!lbase.isShared()) {
                                return lbase.set(zpos, ((ScalarLogicalImpl) value).getLogical());
                            } else {
                                int[] content = new int[bsize];
                                int i = 0;
                                for (; i < zpos; i++) {
                                    content[i] = lbase.getLogical(i);
                                }
                                content[i++] = ((ScalarLogicalImpl) value).getLogical();
                                for (; i < bsize; i++) {
                                    content[i] = lbase.getLogical(i);
                                }
                                return RLogical.RLogicalFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RLogical,ScalarLogical>");
                }
            }
            if (baseTemplate instanceof RList && !subset && !(valueTemplate instanceof RNull)) {
                ValueCopy cpy = new ValueCopy() {
                    @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                        if (!(base instanceof RList) || (value instanceof RNull)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                        RList lbase = (RList) base;
                        int bsize = lbase.size();
                        if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                        int zpos = pos - 1;
                        value.ref();
                        if (!lbase.isShared()) {
                            return lbase.set(zpos, value);
                        } else {
                            RAny[] content = new RAny[bsize];
                            int i = 0;
                            for (; i < zpos; i++) { // shallow copy
                                content[i] = lbase.getRAny(i);
                            }
                            content[i++] = value;
                            for (; i < bsize; i++) { // shallow copy
                                content[i] = lbase.getRAny(i);
                            }
                            return RList.RListFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    }
                };
                return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RList,?>");
            }
            if (baseTemplate instanceof RString && valueTemplate instanceof ScalarStringImpl) {
                ValueCopy cpy = new ValueCopy() {
                    @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                        if (!(base instanceof RString) || !(value instanceof ScalarStringImpl)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                        RString sbase = (RString) base;
                        int bsize = sbase.size();
                        if (pos < 1 || pos > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                        int zpos = pos - 1;
                        if (!sbase.isShared()) {
                            return sbase.set(zpos, ((ScalarStringImpl) value).getString());
                        } else {
                            String[] content = new String[bsize];
                            int i = 0;
                            for (; i < zpos; i++) {
                                content[i] = sbase.getString(i);
                            }
                            content[i++] = ((ScalarStringImpl) value).getString();
                            for (; i < bsize; i++) {
                                content[i] = sbase.getString(i);
                            }
                            return RString.RStringFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    }
                };
                return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RString,ScalarString>");
            }
            return null;
        }

        // FIXME: the asXXX functions will allocate boxes, probably should create asXXXScalar() casts
        public static RAny genericUpdate(RArray base, int pos, RAny value, boolean subset, ASTNode ast) {
            // FIXME: avoid some copying here but careful about lists
            RArray typedBase;
            Object rawValue;
            int[] dimensions = base.dimensions();
            Names names = base.names();

            if (value instanceof RList) { // FIXME: this code gets copied around a few times, could it be refactored without a performance penalty?
                if (base instanceof RList) {
                    typedBase = base;
                } else {
                    typedBase = base.asList();
                    dimensions = null;
                }
                RAny v = subset ? ((RList) value).getRAny(0) : value;
                v.ref();
                rawValue = v;
            } else if (base instanceof RList) {
                typedBase = base;
                rawValue = value;
                value.ref();
            } else if (base instanceof RRaw) {
                if (value instanceof RRaw) {
                    typedBase = base;
                    rawValue = ((RRaw) value).get(0);
                } else {
                    throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                }
            } else if (value instanceof RRaw) {
                throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
            } else if (base instanceof RString || value instanceof RString) {
                typedBase = base.asString();
                rawValue = value.asString().get(0);
            } else if (base instanceof RComplex || value instanceof RComplex) {
                typedBase = base.asComplex();
                rawValue = value.asComplex().get(0);
            } else if (base instanceof RDouble || value instanceof RDouble) {
                typedBase = base.asDouble();
                rawValue = value.asDouble().get(0);
            } else if (base instanceof RInt || value instanceof RInt) {
                typedBase = base.asInt();
                rawValue = value.asInt().get(0);
            } else {
                assert Utils.check(base instanceof RLogical || base instanceof RNull);
                assert Utils.check(value instanceof RLogical);
                typedBase = base.asLogical();
                rawValue = ((RLogical) value).get(0);
            }
            int bsize = base.size();
            if (pos > 0) {
                if (pos <= bsize) {
                    int zpos = pos - 1;
                    if (base == typedBase && !base.isShared()) {
                        base.set(zpos, rawValue);
                        return base;
                    }
                    RArray res = Utils.createArray(typedBase, bsize, dimensions, names, base.attributesRef());
                    int i = 0;
                    for (; i < zpos; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    res.set(i++, rawValue);
                    for (; i < bsize; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    return res;
                } else {
                    int zpos = pos - 1;
                    int nsize = zpos + 1;
                    RArray res = Utils.createArray(typedBase, nsize, names != null).setAttributes(base.attributesRef()); // drop
                                                                                                                         // dimensions
                    int i = 0;
                    for (; i < bsize; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    for (; i < zpos; i++) {
                        Utils.setNA(res, i);
                    }
                    res.set(i, rawValue);
                    if (names != null) {
                        res = res.setNames(expandNames(names, zpos + 1));
                    }
                    return res;
                }
            } else { // pos <= 0
                if (pos == RInt.NA) {
                    if (subset) {
                        return typedBase;
                    } else {
                        throw RError.getSelectMoreThanOne(ast);
                    }
                }
                if (!subset) {
                    if (bsize <= 1 || pos == 0) { throw RError.getSelectLessThanOne(ast); }
                    if (bsize > 2) { throw RError.getSelectMoreThanOne(ast); }
                    // bsize == 2
                    if (pos != -1 && pos != -2) { throw RError.getSelectMoreThanOne(ast); }
                }
                if (pos == 0) { return typedBase; }
                int keep = -pos - 1;
                Utils.refIfRAny(rawValue); // ref once again to make sure it is treated as shared
                RArray res = Utils.createArray(typedBase, bsize, dimensions, names, base.attributesRef());
                int i = 0;

                if (keep >= bsize) {
                    // update all elements of the vector
                    for (; i < bsize; i++) {
                        res.set(i, rawValue);
                    }
                    return res;
                }

                for (; i < keep; i++) {
                    res.set(i, rawValue);
                }
                res.set(i, typedBase.get(i));
                i++;
                for (; i < bsize; i++) {
                    res.set(i, rawValue);
                }
                return res;
            }
        }

        public Specialized createGeneric() {
            ValueCopy cpy = new ValueCopy() {
                @Override RAny copy(RArray base, int pos, RAny value) throws SpecializationException {
                    if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                    RArray avalue = (RArray) value;
                    int vsize = avalue.size();
                    if (vsize != 1) { throw new SpecializationException(Failure.NOT_ONE_ELEMENT_VALUE); }
                    return genericUpdate(base, pos, value, subset, ast);
                }
            };
            return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<Generic>");
        }

        static class Specialized extends ScalarNumericSelection {
            final ValueCopy copy;
            final String dbg;

            Specialized(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset, ValueCopy copy, String dbg) {
                super(ast, isSuper, var, lhs, indexes, rhs, subset);
                this.copy = copy;
                this.dbg = dbg;
            }

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                if (DEBUG_UP) Utils.debug("update - executing ScalarNumericSelection" + dbg);
                try {
                    if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                    RArray abase = (RArray) base;
                    int pos;
                    if (index instanceof ScalarIntImpl) {
                        pos = ((ScalarIntImpl) index).getInt();
                    } else if (index instanceof ScalarDoubleImpl) {
                        pos = Convert.double2int(((ScalarDoubleImpl) index).getDouble());
                    } else {
                        throw new SpecializationException(null);
                    }
                    return copy.copy(abase, pos, value);

                } catch (SpecializationException e) {
                    Failure f = (Failure) e.getResult();
                    if (f == null) {
                        if ((index instanceof RArray) && (((RArray) index).size() != 1)) {
                            f = Failure.NOT_ONE_ELEMENT_INDEX;
                        } else {
                            f = Failure.NOT_NUMERIC_INDEX;
                        }
                    }
                    if (DEBUG_UP) Utils.debug("update - ScalarNumericSelection" + dbg + " failed: " + f);
                    switch (f) {
                    case INDEX_OUT_OF_BOUNDS:
                    case UNEXPECTED_TYPE:
                        Specialized sn = createGeneric();
                        replace(sn, "specialize ScalarNumericSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with ScalarNumericSelection.Generic");
                        return sn.execute(base, index, value);

                    case NOT_ONE_ELEMENT_INDEX:
                        if (!subset) {
                            Subscript s = new Subscript(ast, isSuper, var, lhs, indexes, rhs, subset);
                            replace(s, "install Subscript from ScalarNumericSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with Subscript");
                            return s.execute(base, index, value);
                        }
                        // propagate below

                    default:
                        GenericScalarSelection gs = new GenericScalarSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                        replace(gs, "install GenericScalarSelection from ScalarNumericSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with GenericScalarSelection");
                        return gs.execute(base, index, value);
                    }
                }
            }
        }
    }

    public static class ScalarStringSelection {
        public static RArray genericUpdate(RArray base, String index, RAny value, boolean subset, ASTNode ast) {
            RArray typedBase;
            Object rawValue;
            int[] dimensions = base.dimensions();
            Names names = base.names();

            if (value instanceof RList) { // FIXME: this code gets copied around a few times, could it be refactored without a performance penalty?
                if (base instanceof RList) {
                    typedBase = base;
                } else {
                    typedBase = base.asList();
                    dimensions = null;
                }
                RAny v = subset ? ((RList) value).getRAny(0) : value;
                v.ref();
                rawValue = v;
            } else if (base instanceof RList) {
                typedBase = base;
                rawValue = value;
                value.ref();
            } else if (base instanceof RRaw) {
                if (value instanceof RRaw) {
                    typedBase = base;
                    rawValue = ((RRaw) value).get(0);
                } else {
                    throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                }
            } else if (value instanceof RRaw) {
                throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
            } else if (base instanceof RString || value instanceof RString) {
                typedBase = base.asString();
                rawValue = value.asString().get(0);
            } else if (base instanceof RComplex || value instanceof RComplex) {
                typedBase = base.asComplex();
                rawValue = value.asComplex().get(0);
            } else if (base instanceof RDouble || value instanceof RDouble) {
                typedBase = base.asDouble();
                rawValue = value.asDouble().get(0);
            } else if (base instanceof RInt || value instanceof RInt) {
                typedBase = base.asInt();
                rawValue = value.asInt().get(0);
            } else {
                assert Utils.check(base instanceof RLogical || base instanceof RNull);
                assert Utils.check(value instanceof RLogical);
                typedBase = base.asLogical();
                rawValue = ((RLogical) value).get(0);
            }
            int bsize = base.size();
            int pos = -1;
            RSymbol symbol = RSymbol.getSymbol(index);
            if (names != null) {
                pos = names.map(symbol);
            }
            if (pos != -1) {
                // updating
                if (base == typedBase && !base.isShared()) {
                    base.set(pos, rawValue);
                    return base;
                }
                RArray res = Utils.createArray(typedBase, bsize, dimensions, names, base.attributesRef());
                int i = 0;
                for (; i < pos; i++) {
                    res.set(i, typedBase.get(i));
                }
                res.set(i++, rawValue);
                for (; i < bsize; i++) {
                    res.set(i, typedBase.get(i));
                }
                return res;
            }
            // pos == -1
            // appending, if names are empty, create them - this is for appending to empty lists and vectors

            RArray.Names newNames;

            if (names == null) {
                RSymbol[] newSymbols = new RSymbol[bsize + 1];
                for (int i = 0; i < bsize; ++i) {
                    newSymbols[i] = RSymbol.EMPTY_SYMBOL;
                }
                newSymbols[bsize] = symbol;
                newNames = Names.create(newSymbols);
            } else {
                newNames = appendName(names, symbol);
            }
            RArray res = Utils.createArray(typedBase, bsize + 1, dimensions, newNames, base.attributesRef());
            for (int i = 0; i < bsize; i++) {
                res.set(i, typedBase.get(i));
            }
            res.set(bsize, rawValue);
            return res;
        }
    }

    // any update when the selector is a scalar
    // includes deletion of list elements (FIXME: perhaps could move that out into a special node?)
    // rewrites itself if the update is in fact vector-like (subset with logical
    // index, multi-value subset with negative number index)
    // rewrites for other cases (vector selection)
    // so the contract is that this can handle any subscript with a single-value index
    public static class GenericScalarSelection extends UpdateVector {

        public GenericScalarSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
        }

        public static RAny deleteElement(RList base, int i) {
            return deleteElement(base, i, base.size());
        }

        public static RAny deleteElement(RList base, int index, int size) {
            int j = 0;
            int i = index;
            int nsize = size - 1;
            RAny[] content = new RAny[nsize];
            for (; j < i; j++) { // shallow copy
                content[j] = base.getRAny(j);
            }
            i++;
            for (; j < nsize; j++) { // shallow copy
                content[j] = base.getRAny(i++);
            }
            Names bnames = base.names();
            return RList.RListFactory.getFor(content, null, bnames == null ? null : removeName(bnames, index), base.attributesRef()); // drop dimensions
        }

        public static RAny deleteElement(RList base, int i, ASTNode ast, boolean subset) {
            int size = base.size();
            if (i > 0) {
                int zi = i - 1; // zero-based
                if (i <= size) {
                    // remove element i
                    return deleteElement(base, zi, size);
                } else if (subset) {
                    // note that we could have this branch just for "i > size + 1", however, not quite, because
                    // when i == size + 1, subset drops dimensions
                    int j = 0;
                    int nsize = i - 1;
                    RAny[] content = new RAny[nsize];
                    for (; j < size; j++) { // shallow copy
                        content[j] = base.getRAny(j);
                    }
                    for (; j < nsize; j++) {
                        content[j] = RList.NULL;
                    }
                    Names bnames = base.names();
                    return RList.RListFactory.getFor(content, null, bnames == null ? null : expandNames(bnames, nsize), base.attributesRef());
                    // drop dimensions
                } else {
                    return base;
                }
            }

            if (subset) {
                if (i == 0 || i == RInt.NA) { return base; }
            } else {
                if (i == 0) {
                    throw RError.getSelectLessThanOne(ast);
                } else if (i == RInt.NA) { throw RError.getSelectMoreThanOne(ast); }
            }
            // i < 0
            if (!subset) {
                if (size <= 1) { throw RError.getSelectLessThanOne(ast); }
                if (size > 2) { throw RError.getSelectMoreThanOne(ast); }
                if (i != -1 && i != -2) { throw RError.getSelectMoreThanOne(ast); }
            }
            int keep = -i - 1;
            return RList.RListFactory.getScalar(base.getRAny(keep)); // shallow
                                                                     // copy
        }

        public static RAny deleteElement(RList base, String index) {
            Names names = base.names();
            int deleteIndex = -1;
            if (names != null) {
                deleteIndex = names.map(RSymbol.getSymbol(index));
            }
            if (deleteIndex != -1) {
                return deleteElement(base, deleteIndex);
            } else {
                return Utils.dropDimensions(base);
            }
        }

        public static RAny deleteElement(RArray base, RArray index, ASTNode ast, boolean subset) {
            assert Utils.check(!(index instanceof RNull));
            if (!(base instanceof RList)) {
                if (subset) {
                    throw RError.getReplacementZero(ast);
                } else {
                    throw RError.getMoreElementsSupplied(ast);
                }
            }
            RList l = (RList) base;
            int i;
            if (index instanceof RInt) {
                i = ((RInt) index).getInt(0);
                return deleteElement(l, i, ast, subset);
            } else if (index instanceof RDouble) {
                i = Convert.double2int(((RDouble) index).getDouble(0));
                return deleteElement(l, i, ast, subset);
            } else if (index instanceof RLogical) {
                i = Convert.logical2int(((RLogical) index).getLogical(0));
                if (subset) {
                    if (i == RLogical.TRUE) {
                        return RList.EMPTY;
                    } else {
                        return base;
                    }
                }
                return deleteElement(l, i, ast, subset);
            } else if (index instanceof RString) {
                return deleteElement(l, ((RString) index).getString(0));
            } else {
                throw RError.getInvalidSubscriptType(ast, index.typeOf());
            }
        }

        public static RAny update(RArray base, RArray index, RArray value, ASTNode ast, boolean subset) throws SpecializationException {
            int vsize = value.size();
            if (vsize == 0) { throw RError.getReplacementZero(ast); }
            if (index instanceof RString) { return ScalarStringSelection.genericUpdate(base, ((RString) index).getString(0), value, subset, ast); }
            if (index instanceof RLogical) {
                if (subset) { throw new SpecializationException(Failure.MAYBE_VECTOR_UPDATE); }
                if (vsize > 1 && !(base instanceof RList)) { throw RError.getMoreElementsSupplied(ast); }
                int l = ((RLogical) index).getLogical(0);
                if (l == RLogical.FALSE) { throw RError.getSelectLessThanOne(ast); }
                if (l == RLogical.NA) { throw RError.getSelectMoreThanOne(ast); }
                return ScalarNumericSelection.genericUpdate(base, RLogical.TRUE, value, subset, ast);
            }
            int i;
            if (index instanceof RInt) {
                i = ((RInt) index).getInt(0);
            } else if (index instanceof RDouble) {
                i = Convert.double2int(((RDouble) index).getDouble(0));
            } else {
                throw RError.getInvalidSubscriptType(ast, index.typeOf());
            }
            if (i >= 0 || i == RInt.NA || !subset) {
                if (vsize > 1) {
                    if (subset) {
                        if (i == RInt.NA) {
                            throw RError.getNASubscripted(ast);
                        } else {
                            RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                        }
                    } else {
                        if (!(base instanceof RList)) { throw RError.getMoreElementsSupplied(ast); }
                    }
                }
                return ScalarNumericSelection.genericUpdate(base, i, value, subset, ast);
            } else {
                // subset with negative index
                if (vsize == 1) {
                    return ScalarNumericSelection.genericUpdate(base, i, value, subset, ast);
                } else {
                    throw new SpecializationException(Failure.MAYBE_VECTOR_UPDATE);
                }
            }
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing GenericScalarSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (!(index instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_INDEX); }
                RArray aindex = (RArray) index;
                int isize = aindex.size();
                if (isize != 1) { throw new SpecializationException(Failure.NOT_ONE_ELEMENT_INDEX); }
                if (value instanceof RNull) { return deleteElement(abase, aindex, ast, subset); }
                if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                RArray avalue = (RArray) value;
                return update(abase, aindex, avalue, ast, subset);

            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_UP) Utils.debug("update - GenericScalarSelection failed: " + f);
                switch (f) {
                case MAYBE_VECTOR_UPDATE:
                case NOT_ONE_ELEMENT_INDEX:
                    if (subset) {
                        if (IntImpl.RIntSimpleRange.isInstance(index)) {
                            IntSimpleRangeSelection is = new IntSimpleRangeSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                            replace(is, "install IntSimpleRangeSelection from GenericScalarSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSimpleRangeSelection");
                            return is.execute(base, index, value);
                        }
                        if (IntImpl.RIntSequence.isInstance(index)) {
                            IntSequenceSelection is = new IntSequenceSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                            replace(is, "install IntSequenceSelection from GenericScalarSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSequenceSelection");
                            return is.execute(base, index, value);
                        }
                        if (index instanceof RInt || index instanceof RDouble) {
                            NumericSelection ns = new NumericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                            replace(ns, "install NumericSelection from GenericScalarSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with NumericSelection");
                            return ns.execute(base, index, value);
                        }
                        if (index instanceof RLogical) {
                            LogicalSelection ls = new LogicalSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                            replace(ls, "install LogicalSelection from GenericScalarSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with LogicalSelection");
                            return ls.execute(base, index, value);
                        }
                    } else {
                        Subscript s = new Subscript(ast, isSuper, var, lhs, indexes, rhs, subset);
                        replace(s, "install Subscript from GenericScalarSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with Subscript");
                        return s.execute(base, index, value);
                    }
                    // propagate below

                default:
                    GenericSelection gs = new GenericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                    replace(gs, "install GenericSelection from GenericScalarSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with GenericSelection");
                    return gs.execute(base, index, value);
                }
            }
        }
    }

    // for updates where the index is an int sequence
    // specializes for types (base, value) in simple cases
    // handles also some simple cases when types change or when type-conversion
    // of base is needed
    // rewrites itself for more complicated cases
    public static class IntSequenceSelection extends UpdateVector {

        public IntSequenceSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
            assert Utils.check(subset);
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing IntSequenceSelection (uninitialized)");

            try {
                throw new SpecializationException(null);
            } catch (SpecializationException e) {
                Specialized sn = createSimple(base, value);
                if (sn != null) {
                    replace(sn, "specialize IntSequenceSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSequenceSelection.Simple");
                    return sn.execute(base, index, value);
                } else {
                    sn = createExtended();
                    replace(sn, "specialize IntSequenceSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSequenceSelection.Extended");
                    return sn.execute(base, index, value);
                }
            }
        }

        abstract class ValueCopy {
            abstract RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException;
        }

        // specialized for type combinations (base vector, value written)
        // TODO: avoid copying when the base is non-shared and the index, rhs don't depend on it
        public Specialized createSimple(RAny baseTemplate, RAny valueTemplate) {
            // FIXME: could reduce copying when value is not shared
            if (baseTemplate instanceof RList) {
                if (valueTemplate instanceof RList || valueTemplate instanceof RDouble || valueTemplate instanceof RInt || valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException {
                            if (!(base instanceof RList)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RList typedBase = (RList) base;
                            RList typedValue;
                            if (value instanceof RList) {
                                typedValue = (RList) value;
                            } else if (value instanceof RDouble || value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asList();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int imin = index.min();
                            int imax = index.max();
                            if (imin < 1 || imax > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            imin--;
                            imax--; // convert to 0-based
                            int isize = index.size();
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            RAny[] content = new RAny[bsize];
                            int i = 0;
                            for (; i < imin; i++) { // shallow copy
                                content[i] = typedBase.getRAny(i);
                            }
                            i = index.from() - 1; // -1 for 0-based
                            int step = index.step();
                            int astep;
                            int delta;
                            if (step > 0) {
                                astep = step;
                                delta = 1;
                            } else {
                                astep = -step;
                                delta = -1;
                            }
                            int steps = 0;
                            assert Utils.check(steps < isize);
                            for (;;) {
                                content[i] = typedValue.getRAnyRef(steps); // shallow copy
                                i += delta;
                                steps++;
                                if (steps < isize) {
                                    for (int j = 1; j < astep; j++) {
                                        content[i] = typedBase.getRAny(i); // shallow copy
                                        i += delta;
                                    }
                                } else {
                                    break;
                                }
                            }
                            for (i = imax + 1; i < bsize; i++) { // shallow copy
                                content[i] = typedBase.getRAny(i);
                            }
                            return RList.RListFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RList,RList|RDouble|RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RDouble) {
                if (valueTemplate instanceof RDouble || valueTemplate instanceof RLogical || valueTemplate instanceof RInt) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException {
                            if (!(base instanceof RDouble)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RDouble typedBase = (RDouble) base;
                            RDouble typedValue;
                            if (value instanceof RDouble) {
                                typedValue = (RDouble) value;
                            } else if (value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asDouble();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int imin = index.min();
                            int imax = index.max();
                            if (imin < 1 || imax > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            imin--;
                            imax--; // convert to 0-based
                            int isize = index.size();
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            double[] content = new double[bsize];
                            int i = 0;
                            for (; i < imin; i++) {
                                content[i] = typedBase.getDouble(i);
                            }
                            i = index.from() - 1; // -1 for 0-based
                            int step = index.step();
                            int astep;
                            int delta;
                            if (step > 0) {
                                astep = step;
                                delta = 1;
                            } else {
                                astep = -step;
                                delta = -1;
                            }
                            int steps = 0;
                            assert Utils.check(steps < isize);
                            for (;;) {
                                content[i] = typedValue.getDouble(steps);
                                i += delta;
                                steps++;
                                if (steps < isize) {
                                    for (int j = 1; j < astep; j++) {
                                        content[i] = typedBase.getDouble(i);
                                        i += delta;
                                    }
                                } else {
                                    break;
                                }
                            }
                            for (i = imax + 1; i < bsize; i++) {
                                content[i] = typedBase.getDouble(i);
                            }
                            return RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RDouble,RDouble|RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RInt) {
                if (valueTemplate instanceof RInt || valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException {
                            if (!(base instanceof RInt)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RInt typedBase = (RInt) base;
                            RInt typedValue;
                            if (value instanceof RInt) {
                                typedValue = (RInt) value;
                            } else if (value instanceof RLogical) {
                                typedValue = value.asInt();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int imin = index.min();
                            int imax = index.max();
                            if (imin < 1 || imax > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            imin--;
                            imax--; // convert to 0-based
                            int isize = index.size();
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            int[] content = new int[bsize];
                            int i = 0;
                            for (; i < imin; i++) {
                                content[i] = typedBase.getInt(i);
                            }
                            i = index.from() - 1; // -1 for 0-based
                            int step = index.step();
                            int astep;
                            int delta;
                            if (step > 0) {
                                astep = step;
                                delta = 1;
                            } else {
                                astep = -step;
                                delta = -1;
                            }
                            int steps = 0;
                            assert Utils.check(steps < isize);
                            for (;;) {
                                content[i] = typedValue.getInt(steps);
                                i += delta;
                                steps++;
                                if (steps < isize) {
                                    for (int j = 1; j < astep; j++) {
                                        content[i] = typedBase.getInt(i);
                                        i += delta;
                                    }
                                } else {
                                    break;
                                }
                            }
                            for (i = imax + 1; i < bsize; i++) {
                                content[i] = typedBase.getInt(i);
                            }
                            return RInt.RIntFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RInt,RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RLogical) {
                if (valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException {
                            if (!(base instanceof RLogical && value instanceof RLogical)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RLogical typedBase = (RLogical) base;
                            RLogical typedValue = (RLogical) value;
                            int bsize = base.size();
                            int imin = index.min();
                            int imax = index.max();
                            if (imin < 1 || imax > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            imin--;
                            imax--; // convert to 0-based
                            int isize = index.size();
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            int[] content = new int[bsize];
                            int i = 0;
                            for (; i < imin; i++) {
                                content[i] = typedBase.getLogical(i);
                            }
                            i = index.from() - 1; // -1 for 0-based
                            int step = index.step();
                            int astep;
                            int delta;
                            if (step > 0) {
                                astep = step;
                                delta = 1;
                            } else {
                                astep = -step;
                                delta = -1;
                            }
                            int steps = 0;
                            assert Utils.check(steps < isize);
                            for (;;) {
                                content[i] = typedValue.getLogical(steps);
                                i += delta;
                                steps++;
                                if (steps < isize) {
                                    for (int j = 1; j < astep; j++) {
                                        content[i] = typedBase.getLogical(i);
                                        i += delta;
                                    }
                                } else {
                                    break;
                                }
                            }
                            for (i = imax + 1; i < bsize; i++) {
                                content[i] = typedBase.getLogical(i);
                            }
                            return RLogical.RLogicalFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RLogical,RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RString) {
                if (valueTemplate instanceof RString) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException {
                            if (!(base instanceof RString && value instanceof RString)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RString typedBase = (RString) base;
                            RString typedValue = (RString) value;
                            int bsize = base.size();
                            int imin = index.min();
                            int imax = index.max();
                            if (imin < 1 || imax > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            imin--;
                            imax--; // convert to 0-based
                            int isize = index.size();
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            String[] content = new String[bsize];
                            int i = 0;
                            for (; i < imin; i++) {
                                content[i] = typedBase.getString(i);
                            }
                            i = index.from() - 1; // -1 for 0-based
                            int step = index.step();
                            int astep;
                            int delta;
                            if (step > 0) {
                                astep = step;
                                delta = 1;
                            } else {
                                astep = -step;
                                delta = -1;
                            }
                            int steps = 0;
                            assert Utils.check(steps < isize);
                            for (;;) {
                                content[i] = typedValue.getString(steps);
                                i += delta;
                                steps++;
                                if (steps < isize) {
                                    for (int j = 1; j < astep; j++) {
                                        content[i] = typedBase.getString(i);
                                        i += delta;
                                    }
                                } else {
                                    break;
                                }
                            }
                            for (i = imax + 1; i < bsize; i++) {
                                content[i] = typedBase.getString(i);
                            }
                            return RString.RStringFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RString,RString>");
                }
                return null;
            }
            return null;
        }

        // handles type conversion of base
        public Specialized createExtended() {
            ValueCopy cpy = new ValueCopy() {
                @Override RAny copy(RArray base, IntImpl.RIntSequence index, RArray value) throws SpecializationException {
                    RArray typedBase;
                    RArray typedValue;
                    RList listValue = null;
                    int[] dimensions;

                    if (value instanceof RList) { // FIXME: fragment copied around
                        typedValue = null;
                        listValue = (RList) value;
                        if (base instanceof RList) {
                            typedBase = base;
                            dimensions = base.dimensions();
                        } else {
                            typedBase = base.asList();
                            dimensions = null;
                        }
                    } else {
                        if (base instanceof RList) {
                            typedBase = base;
                            typedValue = value.asList();
                        } else if (base instanceof RRaw) {
                            if (value instanceof RRaw) {
                                typedBase = base;
                                typedValue = value.asRaw();
                            } else {
                                throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                            }
                        } else if (value instanceof RRaw) {
                            throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                        } else if (base instanceof RString || value instanceof RString) {
                            typedBase = base.asString();
                            typedValue = value.asString();
                        } else if (base instanceof RComplex || value instanceof RComplex) {
                            typedBase = base.asComplex();
                            typedValue = value.asComplex();
                        } else if (base instanceof RDouble || value instanceof RDouble) {
                            typedBase = base.asDouble();
                            typedValue = value.asDouble();
                        } else if (base instanceof RInt || value instanceof RInt) {
                            typedBase = base.asInt();
                            typedValue = value.asInt();
                        } else {
                            assert Utils.check(base instanceof RLogical || base instanceof RNull);
                            assert Utils.check(value instanceof RLogical);
                            typedBase = base.asLogical();
                            typedValue = value;
                        }
                        dimensions = typedBase.dimensions();
                    }
                    int bsize = base.size();
                    int imin = index.min();
                    int imax = index.max();
                    if (imin < 1 || imax > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                    imin--;
                    imax--; // convert to 0-based
                    int isize = index.size();
                    int vsize = typedValue != null ? typedValue.size() : listValue.size();
                    if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                    RArray res = Utils.createArray(typedBase, bsize, dimensions, base.names(), base.attributesRef());
                    int i = 0;
                    for (; i < imin; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    i = index.from() - 1; // -1 for 0-based
                    int step = index.step();
                    int astep;
                    int delta;
                    if (step > 0) {
                        astep = step;
                        delta = 1;
                    } else {
                        astep = -step;
                        delta = -1;
                    }
                    if (typedValue != null) {
                        int steps = 0;
                        assert Utils.check(steps < isize);
                        for (;;) {
                            res.set(i, typedValue.get(steps));
                            i += delta;
                            steps++;
                            if (steps < isize) {
                                for (int j = 1; j < astep; j++) {
                                    res.set(i, typedBase.get(i));
                                    i += delta;
                                }
                            } else {
                                break;
                            }
                        }
                    } else { // list value
                        int steps = 0;
                        assert Utils.check(steps < isize);
                        for (;;) {
                            res.set(i, listValue.getRAnyRef(steps)); // shallow copy
                            i += delta;
                            steps++;
                            if (steps < isize) {
                                for (int j = 1; j < astep; j++) {
                                    res.set(i, typedBase.get(i));
                                    i += delta;
                                }
                            } else {
                                break;
                            }
                        }
                    }
                    for (i = imax + 1; i < bsize; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    return res;
                }
            };
            return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<Extended>");
        }

        class Specialized extends IntSequenceSelection {
            final ValueCopy copy;
            final String dbg;

            Specialized(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset, ValueCopy copy, String dbg) {
                super(ast, isSuper, var, lhs, indexes, rhs, subset);
                this.copy = copy;
                this.dbg = dbg;
            }

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                if (DEBUG_UP) Utils.debug("update - executing IntSequenceSelection" + dbg);
                try {
                    if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                    RArray abase = (RArray) base;
                    if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                    RArray avalue = (RArray) value;
                    if (!IntImpl.RIntSequence.isInstance(index)) { throw new SpecializationException(Failure.NOT_INT_SEQUENCE_INDEX); }
                    return copy.copy(abase, IntImpl.RIntSequence.cast(index), avalue);

                } catch (SpecializationException e) {
                    Failure f = (Failure) e.getResult();
                    if (DEBUG_UP) Utils.debug("update - IntSequenceSelection" + dbg + " failed: " + f);
                    switch (f) {
                    case UNEXPECTED_TYPE:
                        Specialized sn = createExtended();
                        replace(sn, "specialize IntSequenceSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSequenceSelection.Extended");
                        return sn.execute(base, index, value);

                    default:
                        NumericSelection ns = new NumericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                        replace(ns, "install NumericSelection from IntSequenceSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with NumericSelection");
                        return ns.execute(base, index, value);
                    }
                }
            }
        }
    }

    // for updates where the index is an int simple range
    // specializes for types (base, value) in simple cases
    // handles also some simple cases when types change or when type-conversion
    // of base is needed
    // rewrites itself for more complicated cases
    public static class IntSimpleRangeSelection extends UpdateVector {

        public IntSimpleRangeSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
            assert Utils.check(subset);
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing IntSimpleRangeSelection (uninitialized)");

            try {
                throw new SpecializationException(null);
            } catch (SpecializationException e) {
                Specialized sn = createSimple(base, value);
                if (sn != null) {
                    replace(sn, "specialize IntSimpleRangeSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSimpleRangeSelection.Simple");
                    return sn.execute(base, index, value);
                } else {
                    sn = createExtended();
                    replace(sn, "specialize IntSequenceSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSimpleRangeSelection.Extended");
                    return sn.execute(base, index, value);
                }
            }
        }

        abstract class ValueCopy {
            abstract RAny copy(RArray base, int ito, RArray value) throws SpecializationException;
        }

        // specialized for type combinations (base vector, value written)
        // TODO: avoid copying when the base is non-shared and the index, rhs don't depend on it
        public Specialized createSimple(RAny baseTemplate, RAny valueTemplate) {
            // FIXME: could reduce copying when value is not shared
            if (baseTemplate instanceof RList) {
                if (valueTemplate instanceof RList || valueTemplate instanceof RDouble || valueTemplate instanceof RInt || valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int ito, RArray value) throws SpecializationException {
                            if (!(base instanceof RList)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RList typedBase = (RList) base;
                            RList typedValue;
                            if (value instanceof RList) {
                                typedValue = (RList) value;
                            } else if (value instanceof RDouble || value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asList();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            if (ito > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int isize = ito;
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            RAny[] content = new RAny[bsize];
                            int i = 0;
                            for (; i < ito; i++) {
                                content[i] = typedValue.getRAnyRef(i); // shallow copy
                            }
                            for (; i < bsize; i++) { // shallow copy
                                content[i] = typedBase.getRAny(i);
                            }
                            return RList.RListFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RList,RList|RDouble|RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RDouble) {
                if (valueTemplate instanceof RDouble || valueTemplate instanceof RLogical || valueTemplate instanceof RInt) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int ito, RArray value) throws SpecializationException {
                            if (!(base instanceof RDouble)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RDouble typedBase = (RDouble) base;
                            RDouble typedValue;
                            if (value instanceof RDouble) {
                                typedValue = (RDouble) value;
                            } else if (value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asDouble();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            if (ito > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int isize = ito;
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            double[] content = new double[bsize];
                            int i = 0;
                            for (; i < ito; i++) {
                                content[i] = typedValue.getDouble(i);
                            }
                            for (; i < bsize; i++) {
                                content[i] = typedBase.getDouble(i);
                            }
                            return RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RDouble,RDouble|RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RInt) {
                if (valueTemplate instanceof RInt || valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int ito, RArray value) throws SpecializationException {
                            if (!(base instanceof RInt)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RInt typedBase = (RInt) base;
                            RInt typedValue;
                            if (value instanceof RInt) {
                                typedValue = (RInt) value;
                            } else if (value instanceof RLogical) {
                                typedValue = value.asInt();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            if (ito > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int isize = ito;
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            int[] content = new int[bsize];
                            int i = 0;
                            for (; i < ito; i++) {
                                content[i] = typedValue.getInt(i);
                            }
                            for (; i < bsize; i++) {
                                content[i] = typedBase.getInt(i);
                            }
                            return RInt.RIntFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RInt,RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RLogical) {
                if (valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int ito, RArray value) throws SpecializationException {
                            if (!(base instanceof RLogical && value instanceof RLogical)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RLogical typedBase = (RLogical) base;
                            RLogical typedValue = (RLogical) value;
                            int bsize = base.size();
                            if (ito > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int isize = ito;
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            int[] content = new int[bsize];
                            int i = 0;
                            for (; i < ito; i++) {
                                content[i] = typedValue.getLogical(i);
                            }
                            for (; i < bsize; i++) {
                                content[i] = typedBase.getLogical(i);
                            }
                            return RLogical.RLogicalFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RLogical,RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RString) {
                if (valueTemplate instanceof RString) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, int ito, RArray value) throws SpecializationException {
                            if (!(base instanceof RString && value instanceof RString)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RString typedBase = (RString) base;
                            RString typedValue = (RString) value;
                            int bsize = base.size();
                            if (ito > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int isize = ito;
                            int vsize = typedValue.size();
                            if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                            String[] content = new String[bsize];
                            int i = 0;
                            for (; i < ito; i++) {
                                content[i] = typedValue.getString(i);
                            }
                            for (; i < bsize; i++) {
                                content[i] = typedBase.getString(i);
                            }
                            return RString.RStringFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RString,RString>");
                }
                return null;
            }
            return null;
        }

        // handles type conversion of base
        public Specialized createExtended() {
            ValueCopy cpy = new ValueCopy() {
                @Override RAny copy(RArray base, int ito, RArray value) throws SpecializationException {
                    RArray typedBase;
                    RArray typedValue;
                    RList listValue = null;
                    int[] dimensions;

                    if (value instanceof RList) { // FIXME: fragment copied around
                        typedValue = null;
                        listValue = (RList) value;
                        if (base instanceof RList) {
                            typedBase = base;
                            dimensions = base.dimensions();
                        } else {
                            typedBase = base.asList();
                            dimensions = null;
                        }
                    } else {
                        if (base instanceof RList) {
                            typedBase = base;
                            typedValue = value.asList();
                        } else if (base instanceof RRaw) {
                            if (value instanceof RRaw) {
                                typedBase = base;
                                typedValue = value.asRaw();
                            } else {
                                throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                            }
                        } else if (value instanceof RRaw) {
                            throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                        } else if (base instanceof RString || value instanceof RString) {
                            typedBase = base.asString();
                            typedValue = value.asString();
                        } else if (base instanceof RComplex || value instanceof RComplex) {
                            typedBase = base.asComplex();
                            typedValue = value.asComplex();
                        } else if (base instanceof RDouble || value instanceof RDouble) {
                            typedBase = base.asDouble();
                            typedValue = value.asDouble();
                        } else if (base instanceof RInt || value instanceof RInt) {
                            typedBase = base.asInt();
                            typedValue = value.asInt();
                        } else {
                            assert Utils.check(base instanceof RLogical || base instanceof RNull);
                            assert Utils.check(value instanceof RLogical);
                            typedBase = base.asLogical();
                            typedValue = value;
                        }
                        dimensions = typedBase.dimensions();
                    }
                    int bsize = base.size();
                    if (ito > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                    int isize = ito;
                    int vsize = typedValue != null ? typedValue.size() : listValue.size();
                    if (isize != vsize) { throw new SpecializationException(Failure.NOT_SAME_LENGTH); }
                    RArray res = Utils.createArray(typedBase, bsize, dimensions, base.names(), base.attributesRef());
                    int i = 0;
                    if (typedValue != null) {
                        for (; i < ito; i++) {
                            res.set(i, typedValue.get(i));
                        }
                    } else {
                        for (; i < ito; i++) { // shallow copy
                            res.set(i, listValue.getRAnyRef(i));
                        }
                    }
                    for (; i < bsize; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    return res;
                }
            };
            return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<Extended>");
        }

        class Specialized extends IntSimpleRangeSelection {
            final ValueCopy copy;
            final String dbg;

            Specialized(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset, ValueCopy copy, String dbg) {
                super(ast, isSuper, var, lhs, indexes, rhs, subset);
                this.copy = copy;
                this.dbg = dbg;
            }

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                if (DEBUG_UP) Utils.debug("update - executing IntSequenceSelection" + dbg);
                try {
                    if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                    RArray abase = (RArray) base;
                    if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                    RArray avalue = (RArray) value;
                    if (!IntImpl.RIntSimpleRange.isInstance(index)) { throw new SpecializationException(Failure.NOT_INT_SEQUENCE_INDEX); }
                    return copy.copy(abase, IntImpl.RIntSimpleRange.cast(index).to(), avalue);

                } catch (SpecializationException e) {
                    Failure f = (Failure) e.getResult();
                    if (DEBUG_UP) Utils.debug("update - IntSimpleRangeSelection" + dbg + " failed: " + f);
                    switch (f) {
                    case UNEXPECTED_TYPE:
                        Specialized sn = createExtended();
                        replace(sn, "specialize IntSimpleRangeSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSimpleRangeSelection.Extended");
                        return sn.execute(base, index, value);

                    default:
                        IntSequenceSelection is = new IntSequenceSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                        replace(is, "install IntSequenceSelection from IntSimpleRangeSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with IntSequenceSelection");
                        return is.execute(base, index, value);
                    }
                }
            }
        }
    }

    // for updates where the index is a numeric (int, double) vector
    public static class NumericSelection extends UpdateVector {

        public NumericSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
            assert Utils.check(subset);
        }

        public static RArray deleteElements(RList base, RInt index, ASTNode ast, boolean subset) {
            assert Utils.check(subset);
            boolean hasNegative = false;
            boolean hasPositive = false;
            boolean hasNA = false;
            int bsize = base.size();
            int isize = index.size();
            boolean[] selected = new boolean[bsize];
            int maxIndex = 0;
            int ntrue = 0;

            for (int i = 0; i < isize; i++) {
                int v = index.getInt(i);
                if (v > maxIndex) {
                    maxIndex = v;
                }
                if (v == RInt.NA) {
                    hasNA = true;
                    continue;
                }
                if (v == 0) {
                    continue;
                }
                int vi;
                if (v > 0) {
                    hasPositive = true;
                    vi = v - 1;
                } else {
                    hasNegative = true;
                    vi = -v - 1;
                }
                if (vi < selected.length) {
                    if (!selected[vi]) {
                        ntrue++;
                        selected[vi] = true;
                    }
                }
            }
            Names names = base.names();
            RSymbol[] symbols = names == null ? null : names.sequence();

            if (!hasNegative) {
                int nullsToAdd = 0;
                if (maxIndex > (bsize + 1)) {
                    // there were indexes "above" the base vector, but perhaps not all were mentioned
                    // for all non-mentioned we have to add NULL at the end of the new list
                    final int aboveSize = maxIndex - bsize;
                    boolean[] aboveSelected = new boolean[aboveSize];
                    int natrue = 0;

                    for (int i = 0; i < isize; i++) {
                        int v = index.getInt(i);
                        if (v > bsize) { // note RInt.NA < 0, bsize >= 0
                            int vi = v - bsize - 1;
                            if (!aboveSelected[vi]) {
                                aboveSelected[vi] = true;
                                natrue++;
                            }
                        }
                    }
                    nullsToAdd = aboveSize - natrue;
                }
                int nsize = (bsize - ntrue) + nullsToAdd;
                RAny[] content = new RAny[nsize];
                RSymbol[] nsymbols = symbols == null ? null : new RSymbol[nsize];
                int j = 0;

                for (int i = 0; i < bsize; i++) {
                    if (!selected[i]) { // shallow copy
                        content[j] = base.getRAny(i);
                        if (symbols != null) {
                            nsymbols[j] = symbols[i];
                        }
                        j++;
                    }
                }
                for (int i = 0; i < nullsToAdd; i++) {
                    content[j] = RList.NULL;
                    if (symbols != null) {
                        nsymbols[j] = RSymbol.EMPTY_SYMBOL;
                    }
                    j++;
                }
                int[] dimensions = null;
                if (nsize == bsize && maxIndex <= bsize) {
                    dimensions = base.dimensions();
                }
                return RList.RListFactory.getFor(content, dimensions, nsymbols == null ? null : Names.create(nsymbols), base.attributesRef());
            } else {
                // hasNegative == true
                if (hasPositive || hasNA) { throw RError.getOnlyZeroMixed(ast); }
                int nsize = ntrue;
                RAny[] content = new RAny[nsize];
                RSymbol[] nsymbols = symbols == null ? null : new RSymbol[nsize];
                int j = 0;

                for (int i = 0; i < bsize; i++) {
                    if (selected[i]) { // shallow copy
                        content[j] = base.getRAny(i);
                        if (symbols != null) {
                            nsymbols[j] = symbols[i];
                        }
                        j++;
                    }
                }
                int[] dimensions = nsize == bsize ? base.dimensions() : null;
                return RList.RListFactory.getFor(content, dimensions, nsymbols == null ? null : Names.create(nsymbols), base.attributesRef());
            }
        }

        public static RArray genericUpdate(RArray base, RInt indexArg, RAny value, ASTNode ast, boolean subset) {
            assert Utils.check(subset);
            RArray typedBase;
            RArray typedValue;
            final boolean listBase = base instanceof RList;
            RList listValue = null;
            int[] dimensions;

            RInt index;
            if (indexArg instanceof View.ParametricView) {
                index = indexArg.materialize();
            } else {
                index = indexArg;
            }

            if (value instanceof RNull) { // FIXME: fragment copied around
                if (listBase) {
                    return deleteElements((RList) base, index, ast, subset);
                } else {
                    if (index.size() == 0) {
                        return base;
                    } else {
                        throw RError.getReplacementZero(ast);
                    }
                }
            } else if (value instanceof RList) {
                listValue = (RList) value;
                typedValue = null;
                if (listBase) {
                    typedBase = base;
                    dimensions = base.dimensions();
                } else {
                    typedBase = base.asList();
                    dimensions = null;
                }
            } else {
                dimensions = base.dimensions();
                if (listBase) {
                    typedBase = base;
                    listValue = value.asList();
                    typedValue = null;
                } else if (base instanceof RRaw) {
                    if (value instanceof RRaw) {
                        typedBase = base;
                        typedValue = value.asRaw();
                    } else {
                        throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                    }
                } else if (value instanceof RRaw) {
                    throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                } else if (base instanceof RString || value instanceof RString) {
                    typedBase = base.asString();
                    typedValue = value.asString();
                } else if (base instanceof RComplex || value instanceof RComplex) {
                    typedBase = base.asComplex();
                    typedValue = value.asComplex();
                } else if (base instanceof RDouble || value instanceof RDouble) {
                    typedBase = base.asDouble();
                    typedValue = value.asDouble();
                } else if (base instanceof RInt || value instanceof RInt) {
                    typedBase = base.asInt();
                    typedValue = value.asInt();
                } else {
                    assert Utils.check(base instanceof RLogical || base instanceof RNull);
                    assert Utils.check(value instanceof RLogical);
                    typedBase = base.asLogical();
                    typedValue = (RLogical) value;
                }
            }

            boolean hasNegative = false;
            boolean hasPositive = false;
            boolean hasNA = false;
            int bsize = typedBase.size();
            int isize = index.size();
            boolean[] omit = null;
            int maxIndex = 0;
                // FIXME: this is doing two passes through the index vector
            for (int i = 0; i < isize; i++) {
                int v = index.getInt(i);
                if (v > maxIndex) {
                    maxIndex = v;
                }
                if (v == RInt.NA) {
                    hasNA = true;
                    continue;
                }
                if (v > 0) {
                    hasPositive = true;
                    continue;
                }
                if (v < 0) {
                    if (!hasNegative) {
                        hasNegative = true;
                        omit = new boolean[bsize];
                    }
                    int vi = -v - 1;
                    if (vi < omit.length) {
                        if (!omit[vi]) {
                            omit[vi] = true;
                        }
                    }
                }
            }
            int vsize = typedValue != null ? typedValue.size() : listValue.size();
            if (!hasNegative) {
                if (hasNA && vsize > 1) { throw RError.getNASubscripted(ast); }
                int nsize = maxIndex;
                Names names = base.names();
                boolean expanding = false;
                boolean copying = true;
                RArray res;
                if (nsize <= bsize) {
                    nsize = bsize;
                    if (typedBase == base && !typedBase.isShared() && !index.dependsOn(typedBase) &&
                            (typedValue == null || !typedValue.dependsOn(typedBase)) &&
                            (listValue == null || !listValue.dependsOn(typedBase))) {
                        copying = false;
                        res = typedBase;
                    } else {
                        res = Utils.createArray(typedBase, nsize, dimensions, names, base.attributesRef());
                    }
                } else {
                    expanding = true;
                    // drop dimensions
                    res = Utils.createArray(typedBase, nsize, names != null).setAttributes(base.attributesRef());
                }

                // FIXME: this may lead to unnecessary computation and copying if the base is a complex view

                if (copying) { // FIXME: reduce virtual calls in the copy?
                    int i = 0;
                    for (; i < bsize; i++) {
                        res.set(i, typedBase.get(i));
                    }
                    for (; i < nsize; i++) {
                        Utils.setNA(res, i);
                    }
                }
                int j = 0;
                for (int i = 0; i < isize; i++) {
                    int v = index.getInt(i);
                    if (v != 0 && v != RInt.NA) {
                        if (typedValue != null) {
                            res.set(v - 1, typedValue.get(j++));
                        } else {
                            res.set(v - 1, listValue.getRAnyRef(j++));
                        }
                        if (j == vsize) {
                            j = 0;
                        }
                    }
                }
                if (j != 0) {
                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                }
                if (expanding && names != null) {
                    res = res.setNames(expandNames(names, nsize));
                }
                return res;
            } else {
                // hasNegative == true
                if (hasPositive || hasNA) { throw RError.getOnlyZeroMixed(ast); }
                RArray res = Utils.createArray(typedBase, bsize, dimensions, base.names(), base.attributesRef());
                int j = 0;
                for (int i = 0; i < bsize; i++) {
                    if (omit[i]) {
                        res.set(i, typedBase.get(i));
                    } else {
                        if (typedValue != null) {
                            res.set(i, typedValue.get(j++));
                        } else {
                            res.set(i, listValue.getRAnyRef(j++));
                        }
                        if (j == vsize) {
                            j = 0;
                        }
                    }
                }
                return res;
            }
        }

        abstract class ValueUpdate {
            abstract RAny update(RArray base, RInt index, RAny value) throws SpecializationException;
        }

        public Specialized createSimple(RAny baseTemplate, RAny valueTemplate) {

            if (baseTemplate instanceof StringImpl) {
                if (valueTemplate instanceof RString) {
                    ValueUpdate up = new ValueUpdate() {
                        @Override
                        RAny update(RArray base, RInt index, RAny value) throws SpecializationException {
                            if (!(base instanceof StringImpl && value instanceof RString)) {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            RString typedValue = (RString) value;
                            if (base.isShared()) {
                                throw new SpecializationException(Failure.SHARED_BASE);
                            }
                            if (index.dependsOn(base) || value.dependsOn(base)) {
                                throw new SpecializationException(Failure.UNEXPECTED_DEPENDENCY);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            int vsize = typedValue.size();
                            String[] sbase = ((StringImpl) base).getContent();

                            if (!base.isShared() && !index.dependsOn(base) && !value.dependsOn(base)) {
                                RInt mindex;
                                if (index instanceof View.ParametricView) {
                                    mindex = index.materialize();
                                } else {
                                    mindex = index;
                                }
                                for (int i = 0; i < isize; i++) { // note two passes through the index array
                                    int v = mindex.getInt(i);
                                    if (v <= 0 || v > bsize) { // includes RInt.NA
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                }
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = mindex.getInt(i) - 1; // 0-based
                                    sbase[ivalue] = typedValue.getString(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return base;
                            } else {
                                String[] content = new String[bsize]; // TODO: add this also to other types?
                                System.arraycopy(sbase, 0, content, 0, bsize);
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = index.getInt(i); // 1-based
                                    if (ivalue <= 0 || ivalue > bsize) {
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                    content[ivalue - 1] = typedValue.getString(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return RString.RStringFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, up, "<RString,RString>");
                }
            }
            if (baseTemplate instanceof DoubleImpl) {
                if (valueTemplate instanceof RDouble || valueTemplate instanceof RLogical || valueTemplate instanceof RInt) {
                    ValueUpdate up = new ValueUpdate() {
                        @Override
                        RAny update(RArray base, RInt index, RAny value) throws SpecializationException {
                            if (!(base instanceof DoubleImpl)) {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            if (base.isShared()) {
                                throw new SpecializationException(Failure.SHARED_BASE);
                            }
                            if (index.dependsOn(base) || value.dependsOn(base)) {
                                throw new SpecializationException(Failure.UNEXPECTED_DEPENDENCY);
                            }
                            double[] dbase = ((DoubleImpl) base).getContent();
                            RDouble typedValue;
                            if (value instanceof RDouble) {
                                typedValue = (RDouble) value;
                            } else if (value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asDouble();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            int vsize = typedValue.size();

                            if (!base.isShared() && !index.dependsOn(base) && !value.dependsOn(base)) {
                                RInt mindex;
                                if (index instanceof View.ParametricView) {
                                    mindex = index.materialize();
                                } else {
                                    mindex = index;
                                }
                                for (int i = 0; i < isize; i++) { // note two passes through the index array
                                    int v = mindex.getInt(i);
                                    if (v <= 0 || v > bsize) { // includes RInt.NA
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                }
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = mindex.getInt(i) - 1; // 0-based
                                    dbase[ivalue] = typedValue.getDouble(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return base;
                            } else {
                                double[] content = new double[bsize]; // TODO: add this also to other types?
                                System.arraycopy(dbase, 0, content, 0, bsize);
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = index.getInt(i); // 1-based
                                    if (ivalue <= 0 || ivalue > bsize) {
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                    content[ivalue - 1] = typedValue.getDouble(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }

                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, up, "<RDouble,RDouble|RInt|RLogical>");
                }
            }

            if (baseTemplate instanceof IntImpl) {
                if (valueTemplate instanceof RInt || valueTemplate instanceof RLogical) {
                    ValueUpdate up = new ValueUpdate() {
                        @Override
                        RAny update(RArray base, RInt index, RAny value) throws SpecializationException {
                            if (!(base instanceof IntImpl)) {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            RInt typedValue;
                            if (value instanceof RInt) {
                                typedValue = (RInt) value;
                            } else if (value instanceof RLogical) {
                                typedValue = value.asInt();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            int vsize = typedValue.size();
                            int[] ibase = ((IntImpl) base).getContent();

                            if (!base.isShared() && !index.dependsOn(base) && !value.dependsOn(base)) {
                                RInt mindex;
                                if (index instanceof View.ParametricView) {
                                    mindex = index.materialize();
                                } else {
                                    mindex = index;
                                }
                                for (int i = 0; i < isize; i++) { // note two passes through the index array
                                    int v = mindex.getInt(i);
                                    if (v <= 0 || v > bsize) { // includes RInt.NA
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                }
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = mindex.getInt(i) - 1; // 0-based
                                    ibase[ivalue] = typedValue.getInt(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return base;
                            } else {
                                int[] content = new int[bsize]; // TODO: add this also to other types?
                                System.arraycopy(ibase, 0, content, 0, bsize);
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = index.getInt(i); // 1-based
                                    if (ivalue <= 0 || ivalue > bsize) {
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                    content[ivalue - 1] = typedValue.getInt(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return RInt.RIntFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, up, "<RInt,RInt|RLogical>");
                }
            }

            if (baseTemplate instanceof LogicalImpl) {
                if (valueTemplate instanceof RLogical) {
                    ValueUpdate up = new ValueUpdate() {
                        @Override
                        RAny update(RArray base, RInt index, RAny value) throws SpecializationException {
                            if (!(base instanceof LogicalImpl && value instanceof RLogical)) {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            if (base.isShared()) {
                                throw new SpecializationException(Failure.SHARED_BASE);
                            }
                            if (index.dependsOn(base) || value.dependsOn(base)) {
                                throw new SpecializationException(Failure.UNEXPECTED_DEPENDENCY);
                            }
                            int[] lbase = ((LogicalImpl) base).getContent();
                            RLogical typedValue = (RLogical) value;
                            int bsize = base.size();
                            int isize = index.size();
                            int vsize = typedValue.size();

                            if (!base.isShared() && !index.dependsOn(base) && !value.dependsOn(base)) {
                                RInt mindex;
                                if (index instanceof View.ParametricView) {
                                    mindex = index.materialize();
                                } else {
                                    mindex = index;
                                }
                                for (int i = 0; i < isize; i++) { // note two passes through the index array
                                    int v = mindex.getInt(i);
                                    if (v <= 0 || v > bsize) { // includes RInt.NA
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                }
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = mindex.getInt(i) - 1; // 0-based
                                    lbase[ivalue] = typedValue.getLogical(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return base;
                            } else {
                                int[] content = new int[bsize]; // TODO: add this also to other types?
                                System.arraycopy(lbase, 0, content, 0, bsize);
                                int j = 0;
                                for (int i = 0; i < isize; i++) {
                                    int ivalue = index.getInt(i); // 1-based
                                    if (ivalue <= 0 || ivalue > bsize) {
                                        throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS);
                                    }
                                    content[ivalue - 1] = typedValue.getLogical(j);
                                    j++;
                                    if (j == vsize) {
                                        j = 0;
                                    }
                                }
                                if (j != 0) {
                                    RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                                }
                                return RLogical.RLogicalFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, up, "<RLogical,RLogical>");
                }
            }

            return null;
        }

        public Specialized createGeneric() {
            ValueUpdate up = new ValueUpdate() {
                @Override RAny update(RArray base, RInt index, RAny value) {
                    return genericUpdate(base, index, value, ast, true);
                }
            };
            return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, up, "<Generic>");
        }

        class Specialized extends NumericSelection {
            final ValueUpdate update;
            final String dbg;

            Specialized(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset, ValueUpdate update, String dbg) {
                super(ast, isSuper, var, lhs, indexes, rhs, subset);
                this.update = update;
                this.dbg = dbg;
            }

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                if (DEBUG_UP) Utils.debug("update - executing NumericSelection");
                try {
                    if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                    RArray abase = (RArray) base;
                    if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                    RArray avalue = (RArray) value;
                    RInt iindex;
                    if (index instanceof RInt) {
                        iindex = (RInt) index;
                    } else if (index instanceof RDouble) {
                        iindex = index.asInt();
                    } else {
                        throw new SpecializationException(Failure.NOT_NUMERIC_INDEX);
                    }
//                    System.err.println("NUMERIC - base is " + base + " node " + this + " parent " + getParent());
                    return update.update(abase, iindex, avalue);
                } catch (SpecializationException e) {
                    Failure f = (Failure) e.getResult();
                    if (DEBUG_UP) Utils.debug("update - NumericSelection failed: " + f);
                    switch(f) {
                        case UNEXPECTED_TYPE:
                        case SHARED_BASE:
                        case UNEXPECTED_DEPENDENCY:
                        case INDEX_OUT_OF_BOUNDS:
                            Specialized sn = createGeneric();
                            replace(sn, "generalize NumericSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with NumericSelection.Generic");
                            return sn.execute(base, index, value);

                        default:
                            GenericSelection gs = new GenericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                            replace(gs, "install GenericSelection from NumericSelection");
                            if (DEBUG_UP) Utils.debug("update - replaced and re-executing with GenericSelection");
                            return gs.execute(base, index, value);

                    }

                }
            }
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing NumericSelection (uninitialized)");
            try {
                throw new SpecializationException(null);
            } catch (SpecializationException e) {
                Specialized sn = createSimple(base, value);
                if (sn != null) {
                    replace(sn, "specialize NumericSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with NumericSelection.Simple");
                    return sn.execute(base, index, value);
                } else {
                    sn = createGeneric();
                    replace(sn, "specialize NumericSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with NumericSelection.Generic");
                    return sn.execute(base, index, value);
                }
            }
        }

    }

    // for expressions like d[x == c] <- v, where
    //   x is double, d is double, x and d are of the same length
    //   c is a double constant
    //   v is double
    // FIXME: support more combinations
    public static final class LogicalEqualitySelection extends UpdateVector {
        final double c;

        public LogicalEqualitySelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode xExpr, double c, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, new RNode[]{xExpr}, rhs, subset);
            // NOTE: the parent, UpdateVector, will think that xExpr is the index, but that does not matter, it will
            // do the right thing - it will evaluate it and call LogicalEqualitySelection's execute method

            assert Utils.check(subset);
            this.c = c;
        }

        @Override RAny execute(RAny baseArg, RAny xArg, RAny valueArg) {
            try {
                if (!(baseArg instanceof DoubleImpl && xArg instanceof DoubleImpl && valueArg instanceof DoubleImpl)) { throw new SpecializationException(null); }
                RDouble base = (RDouble) baseArg;
                RDouble x = (RDouble) xArg;
                RDouble value = (RDouble) valueArg;
                int size = base.size();
                int vsize = value.size();
                if (x.size() != size) { throw new SpecializationException(null); }
                // FIXME: avoid copying of private base, when the rhs does not depend on it

                boolean hasNA = vsize < 2; // an optimization, we don't care about NAs when vsize < 2

                if (base.isShared()) {
                    throw new SpecializationException(null);
                } else {
                    int vi = 0;
                    double[] baseArr = base.getContent();
                    double[] valueArr = value.getContent();
                    double[] xArr = x.getContent(); // NOTE: xArr or valueArr can be the same as base

                    for (int i = 0; i < size; i++) {
                        double d = xArr[i];
                        if (d == c) {
                            baseArr[i] = valueArr[vi++];
                            if (vi == vsize) {
                                vi = 0;
                            }
                        } else {
                            hasNA = hasNA || RDouble.RDoubleUtils.isNAorNaN(d);
                        }
                    }
                    if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
                    if (vi != 0) {
                        RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                    }
                    return base;
                }

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

                Comparison indexExpr = new Comparison(eq, indexes[0], new Constant(eq.getRHS(), boxedC), r.nodes.exec.Comparison.getEQ());

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

                // index
                return ls.execute(baseArg, (RAny) indexExpr.execute(xArg, boxedC), valueArg);
            }
        }
    }

    // for updates where the index is a logical sequence
    // specializes for types (base, value) in simple cases
    // handles also corner cases and when the type of the base changes
    public static class LogicalSelection extends UpdateVector {

        public LogicalSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
            assert Utils.check(subset);
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing LogicalSelection (uninitialized)");
            try {
                throw new SpecializationException(null);
            } catch (SpecializationException e) {
                Specialized sn = createSimple(base, value);
                if (sn != null) {
                    replace(sn, "specialize LogicalSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with LogicalSelection.Simple");
                    return sn.execute(base, index, value);
                } else {
                    sn = createGeneric();
                    replace(sn, "specialize LogicalSelection");
                    if (DEBUG_UP) Utils.debug("update - replaced and re-executing with LogicalSelection.Generic");
                    return sn.execute(base, index, value);
                }
            }
        }

        abstract class ValueCopy {
            abstract RAny copy(RArray base, RLogical index, RAny value) throws SpecializationException;
        }

        public Specialized createSimple(RAny baseTemplate, RAny valueTemplate) {
            if (baseTemplate instanceof RList) {
                if (valueTemplate instanceof RList || valueTemplate instanceof RDouble || valueTemplate instanceof RLogical || valueTemplate instanceof RInt) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, RLogical index, RAny value) throws SpecializationException {
                            if (!(base instanceof RList)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RList typedBase = (RList) base;
                            RList typedValue;
                            if (value instanceof RList) {
                                typedValue = (RList) value;
                            } else if (value instanceof RDouble || value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asList();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            if (isize > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int vsize = typedValue.size();
                            int ii = 0;
                            int vi = 0;
                            boolean hasNA = false;
                            RList res;
                            if (isize == 0) { return typedBase; }
                            if (!typedBase.isShared() && !typedValue.dependsOn(typedBase) && !index.dependsOn(typedBase) && typedBase.attributes() == null) {
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) { // shallow copy
                                        typedBase.set(bi, typedValue.getRAnyRef(vi));
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                }
                                res = typedBase;
                            } else {
                                RAny[] content = new RAny[bsize];
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) { // shallow copy
                                        content[bi] = typedValue.getRAnyRef(vi);
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                    content[bi] = typedBase.getRAny(bi); // shallow copy
                                }
                                res = RList.RListFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                            if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
                            if (vi != 0) {
                                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                            }
                            return res;
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RList,RList|RDouble|RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RDouble) {
                if (valueTemplate instanceof RDouble || valueTemplate instanceof RLogical || valueTemplate instanceof RInt) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, RLogical index, RAny value) throws SpecializationException {
                            if (!(base instanceof RDouble)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RDouble typedBase = (RDouble) base;
                            RDouble typedValue;
                            if (value instanceof RDouble) {
                                typedValue = (RDouble) value;
                            } else if (value instanceof RInt || value instanceof RLogical) {
                                typedValue = value.asDouble();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            if (isize > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int vsize = typedValue.size();
                            int ii = 0;
                            int vi = 0;
                            boolean hasNA = false;
                            RDouble res;
                            if (isize == 0) { return typedBase; }
                            if (!typedBase.isShared() && !typedValue.dependsOn(typedBase) && !index.dependsOn(typedBase) && typedBase.attributes() == null) {
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        typedBase.set(bi, typedValue.getDouble(vi));
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                }
                                res = typedBase;
                            } else {
                                double[] content = new double[bsize];
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        content[bi] = typedValue.getDouble(vi);
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                    content[bi] = typedBase.getDouble(bi);
                                }
                                res = RDouble.RDoubleFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                            if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
                            if (vi != 0) {
                                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                            }
                            return res;
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RDouble,RDouble|RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RInt) {
                if (valueTemplate instanceof RLogical || valueTemplate instanceof RInt) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, RLogical index, RAny value) throws SpecializationException {
                            if (!(base instanceof RInt)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RInt typedBase = (RInt) base;
                            RInt typedValue;
                            if (value instanceof RInt) {
                                typedValue = (RInt) value;
                            } else if (value instanceof RLogical) {
                                typedValue = value.asInt();
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            if (isize > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int vsize = typedValue.size();
                            int ii = 0;
                            int vi = 0;
                            boolean hasNA = false;
                            RInt res;
                            if (isize == 0) { return typedBase; }
                            if (!typedBase.isShared() && !typedValue.dependsOn(typedBase) && !index.dependsOn(typedBase) && typedBase.attributes() == null) {
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        typedBase.set(bi, typedValue.getInt(vi));
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                }
                                res = typedBase;
                            } else {
                                int[] content = new int[bsize];
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        content[bi] = typedValue.getInt(vi);
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                    content[bi] = typedBase.getInt(bi);
                                }
                                res = RInt.RIntFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                            if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
                            if (vi != 0) {
                                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                            }
                            return res;
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RInt,RInt|RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RLogical) {
                if (valueTemplate instanceof RLogical) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, RLogical index, RAny value) throws SpecializationException {
                            if (!(base instanceof RLogical)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RLogical typedBase = (RLogical) base;
                            RLogical typedValue;
                            if (value instanceof RLogical) {
                                typedValue = (RLogical) value;
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            if (isize > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int vsize = typedValue.size();
                            int[] content = new int[bsize];
                            int ii = 0;
                            int vi = 0;
                            boolean hasNA = false;
                            RLogical res;
                            if (isize == 0) { return typedBase; }
                            if (!typedBase.isShared() && !typedValue.dependsOn(typedBase) && !index.dependsOn(typedBase) && typedBase.attributes() == null) {
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        typedBase.set(bi, typedValue.getLogical(vi));
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                }
                                res = typedBase;
                            } else {
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        content[bi] = typedValue.getLogical(vi);
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                    content[bi] = typedBase.getLogical(bi);
                                }
                                res = RLogical.RLogicalFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                            if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
                            if (vi != 0) {
                                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                            }
                            return res;
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RLogical,RLogical>");
                }
                return null;
            }
            if (baseTemplate instanceof RString) {
                if (valueTemplate instanceof RString) {
                    ValueCopy cpy = new ValueCopy() {
                        @Override RAny copy(RArray base, RLogical index, RAny value) throws SpecializationException {
                            if (!(base instanceof RString)) { throw new SpecializationException(Failure.UNEXPECTED_TYPE); }
                            RString typedBase = (RString) base;
                            RString typedValue;
                            if (value instanceof RString) {
                                typedValue = (RString) value;
                            } else {
                                throw new SpecializationException(Failure.UNEXPECTED_TYPE);
                            }
                            int bsize = base.size();
                            int isize = index.size();
                            if (isize > bsize) { throw new SpecializationException(Failure.INDEX_OUT_OF_BOUNDS); }
                            int vsize = typedValue.size();
                            String[] content = new String[bsize];
                            int ii = 0;
                            int vi = 0;
                            boolean hasNA = false;
                            RString res;
                            if (isize == 0) { return typedBase; }
                            if (!typedBase.isShared() && !typedValue.dependsOn(typedBase) && !index.dependsOn(typedBase) && typedBase.attributes() == null) {
                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        typedBase.set(bi, typedValue.getString(vi));
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                }
                                res = typedBase;
                            } else {

                                for (int bi = 0; bi < bsize; bi++) {
                                    int v = index.getLogical(ii);
                                    ii++;
                                    if (ii == isize) {
                                        ii = 0;
                                    }
                                    if (v == RLogical.TRUE) {
                                        content[bi] = typedValue.getString(vi);
                                        vi++;
                                        if (vi == vsize) {
                                            vi = 0;
                                        }
                                        continue;
                                    }
                                    if (v == RLogical.NA) {
                                        hasNA = true;
                                    }
                                    content[bi] = typedBase.getString(bi);
                                }
                                res = RString.RStringFactory.getFor(content, base.dimensions(), base.names(), base.attributesRef());
                            }
                            if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
                            if (vi != 0) {
                                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
                            }
                            return res;
                        }
                    };
                    return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<RString,RString>");
                }
                return null;
            }
            return null;
        }

        public static RAny deleteElements(RList base, RLogical index, @SuppressWarnings("unused") ASTNode ast) {
            int bsize = base.size();
            int isize = index.size();

            Names names = base.names();
            RSymbol[] symbols = names == null ? null : names.sequence();
            if (isize == bsize) {
                int ntrue = RLogical.RLogicalUtils.truesInRange(index, 0, isize);
                int nsize = bsize - ntrue;
                RAny[] content = new RAny[nsize];
                RSymbol[] nsymbols = symbols == null ? null : new RSymbol[nsize];
                int j = 0;
                for (int i = 0; i < bsize; i++) {
                    int l = index.getLogical(i);
                    if (l != RLogical.TRUE) { // shallow copy
                        content[j] = base.getRAny(i);
                        if (symbols != null) {
                            nsymbols[j] = symbols[i];
                        }
                        j++;
                    }
                }
                return RList.RListFactory.getFor(content, ntrue != 0 ? null : base.dimensions(), nsymbols == null ? null : Names.create(nsymbols), base.attributesRef());
            }
            if (isize > bsize) {
                // for each "non-TRUE" element above base vector size we have to add NULL to the vector
                int ntrue = RLogical.RLogicalUtils.truesInRange(index, 0, bsize);
                int natrue = RLogical.RLogicalUtils.truesInRange(index, bsize, isize);
                int nullsToAdd = isize - bsize - natrue;
                int nsize = (bsize - ntrue) + nullsToAdd;
                RAny[] content = new RAny[nsize];
                RSymbol[] nsymbols = symbols == null ? null : new RSymbol[nsize];
                int j = 0;
                for (int i = 0; i < bsize; i++) {
                    int l = index.getLogical(i);
                    if (l != RLogical.TRUE) { // shallow copy
                        content[j] = base.getRAny(i);
                        if (symbols != null) {
                            nsymbols[j] = symbols[i];
                        }
                        j++;
                    }
                }
                for (int i = 0; i < nullsToAdd; i++) {
                    content[j] = RList.NULL;
                    if (symbols != null) {
                        nsymbols[j] = RSymbol.EMPTY_SYMBOL;
                    }
                    j++;
                }
                return RList.RListFactory.getFor(content, bsize != nsize ? null : base.dimensions(), nsymbols == null ? null : Names.create(nsymbols), base.attributesRef());
            }
            // isize < bsize
            if (isize == 0) { return base; }
            int rep = bsize / isize;
            int lsize = bsize - rep * isize;
            int ntrue = RLogical.RLogicalUtils.truesInRange(index, 0, isize);
            int nltrue = RLogical.RLogicalUtils.truesInRange(index, 0, lsize); // TRUEs in the last cycle of index over base

            int nsize = bsize - (ntrue * rep + nltrue);
            RAny[] content = new RAny[nsize];
            RSymbol[] nsymbols = symbols == null ? null : new RSymbol[nsize];
            int ii = 0;
            int ci = 0;
            for (int bi = 0; bi < bsize; bi++) {
                int l = index.getLogical(ii++);
                if (ii == isize) {
                    ii = 0;
                }
                if (l != RLogical.TRUE) { // shallow copy
                    content[ci] = base.getRAny(bi);
                    if (symbols != null) {
                        nsymbols[ci] = symbols[bi];
                    }
                    ci++;
                }
            }
            return RList.RListFactory.getFor(content, bsize != nsize ? null : base.dimensions(), nsymbols == null ? null : Names.create(nsymbols), base.attributesRef());
        }

        public static RAny genericUpdate(RArray base, RLogical index, RAny value, ASTNode ast) {
            RArray typedBase;
            RArray typedValue;
            RList listValue = null;
            int[] dimensions;

            if (value instanceof RNull) { // FIXME: fragment copied around
                if (base instanceof RList) {
                    return deleteElements((RList) base, index, ast);
                } else {
                    if (index.size() == 0) {
                        return base;
                    } else {
                        throw RError.getReplacementZero(ast);
                    }
                }
            } else if (value instanceof RList) {
                listValue = (RList) value;
                typedValue = null;
                if (base instanceof RList) {
                    typedBase = base;
                    dimensions = base.dimensions();
                } else {
                    typedBase = base.asList();
                    dimensions = null;
                }
            } else {
                dimensions = base.dimensions();
                if (base instanceof RList) {
                    typedBase = base;
                    listValue = value.asList();
                    typedValue = null;
                } else if (base instanceof RRaw) {
                    if (value instanceof RRaw) {
                        typedBase = base;
                        typedValue = value.asRaw();
                    } else {
                        throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                    }
                } else if (value instanceof RRaw) {
                    throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                } else if (base instanceof RString || value instanceof RString) {
                    typedBase = base.asString();
                    typedValue = value.asString();
                } else if (base instanceof RComplex || value instanceof RComplex) {
                    typedBase = base.asComplex();
                    typedValue = value.asComplex();
                } else if (base instanceof RDouble || value instanceof RDouble) {
                    typedBase = base.asDouble();
                    typedValue = value.asDouble();
                } else if (base instanceof RInt || value instanceof RInt) {
                    typedBase = base.asInt();
                    typedValue = value.asInt();
                } else {
                    assert Utils.check(base instanceof RLogical || base instanceof RNull);
                    assert Utils.check(value instanceof RLogical);
                    typedBase = base.asLogical();
                    typedValue = (RLogical) value;
                }
            }
            int bsize = base.size();
            int isize = index.size();
            int vsize = typedValue != null ? typedValue.size() : listValue.size();
            int nsize;
            RArray res;
            Names names = base.names();
            boolean expanding;
            if (isize <= bsize) {
                nsize = bsize;
                expanding = false;
                res = Utils.createArray(typedBase, nsize, dimensions, names, base.attributesRef());
            } else {
                expanding = true;
                // drop dimensions
                nsize = isize;
                res = Utils.createArray(typedBase, nsize, names != null).setAttributes(base.attributesRef());
            }
            int ii = 0;
            int vi = 0;
            boolean hasNA = false;
            for (int ni = 0; ni < nsize; ni++) {
                int v = index.getLogical(ii);
                ii++;
                if (ii == isize) {
                    ii = 0;
                }
                if (v == RLogical.TRUE) {
                    if (typedValue != null) {
                        res.set(ni, typedValue.get(vi));
                    } else {
                        res.set(ni, listValue.get(vi));
                    }
                    vi++;
                    if (vi == vsize) {
                        vi = 0;
                    }
                    continue;
                }
                if (v == RLogical.NA) {
                    hasNA = true;
                }
                if (ni < bsize) {
                    res.set(ni, typedBase.get(ni));
                } else {
                    Utils.setNA(res, ni);
                }
            }
            if (hasNA && vsize >= 2) { throw RError.getNASubscripted(ast); }
            if (vi != 0) {
                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
            }
            if (expanding && names != null) {
                res = res.setNames(expandNames(names, nsize));
            }
            return res;
        }

        public Specialized createGeneric() {
            ValueCopy cpy = new ValueCopy() {
                @Override RAny copy(RArray base, RLogical index, RAny value) {
                    return genericUpdate(base, index, value, ast);
                }
            };
            return new Specialized(ast, isSuper, var, lhs, indexes, rhs, subset, cpy, "<Generic>");
        }

        class Specialized extends LogicalSelection {
            final ValueCopy copy;
            final String dbg;

            Specialized(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset, ValueCopy copy, String dbg) {
                super(ast, isSuper, var, lhs, indexes, rhs, subset);
                this.copy = copy;
                this.dbg = dbg;
            }

            @Override public RAny execute(RAny base, RAny index, RAny value) {
                if (DEBUG_UP) Utils.debug("update - executing LogicalSelection" + dbg);
                try {
                    if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                    RArray abase = (RArray) base;
                    if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                    RArray avalue = (RArray) value;
                    if (!(index instanceof RLogical)) { throw new SpecializationException(Failure.NOT_LOGICAL_INDEX); }
                    RLogical lindex = (RLogical) index;
                    return copy.copy(abase, lindex, avalue);
                } catch (SpecializationException e) {
                    Failure f = (Failure) e.getResult();
                    if (DEBUG_UP) Utils.debug("update - LogicalSelection" + dbg + " failed: " + f);
                    switch (f) {
                    case INDEX_OUT_OF_BOUNDS:
                    case UNEXPECTED_TYPE:
                        Specialized sn = createGeneric();
                        replace(sn, "generalize LogicalSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with LogicalSelection.Generic");
                        return sn.execute(base, index, value);

                    default:
                        GenericSelection gs = new GenericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                        replace(gs, "install GenericSelection from LogicalSelection");
                        if (DEBUG_UP) Utils.debug("update - replaced and re-executing with GenericSelection");
                        return gs.execute(base, index, value);
                    }
                }
            }
        }
    }

    public static class StringSelection {

        public static RAny deleteElements(RList base, RString index, @SuppressWarnings("unused") ASTNode ast) {
            Names bnames = base.names();
            if (bnames == null) { return Utils.dropDimensions(base); }
            int bsize = base.size();
            int isize = index.size();
            boolean[] remove = new boolean[bsize];
            int nremove = 0;

            for (int i = 0; i < isize; i++) {
                RSymbol s = RSymbol.getSymbol(index.getString(i));
                int v = bnames.map(s);
                if (v != -1) {
                    if (!remove[v]) {
                        remove[v] = true;
                        nremove++;
                    }
                }
            }

            if (nremove == 0) { return base; }
            int nsize = bsize - nremove;
            RSymbol[] bsymbols = bnames.sequence();
            RSymbol[] nsymbols = new RSymbol[nsize];
            RAny[] content = new RAny[nsize];
            int j = 0;
            for (int i = 0; i < bsize; i++) {
                if (!remove[i]) { // shallow copy
                    content[j] = base.getRAny(i);
                    nsymbols[j] = bsymbols[i];
                    j++;
                }
            }
            return RList.RListFactory.getFor(content, null, Names.create(nsymbols), base.attributesRef());
        }

        public static RAny genericUpdate(RArray base, RString index, RAny value, ASTNode ast) {

            int isize = index.size();
            if (isize == 1) {
                // this version is faster
                return ScalarStringSelection.genericUpdate(base, index.getString(0), value, true, ast);
            }
            RArray typedBase;
            RArray typedValue;
            RList listValue = null;

            assert Utils.check(!(value instanceof RNull));
            if (value instanceof RList) { // FIXME: fragment copied around
                listValue = (RList) value;
                typedValue = null;
                if (base instanceof RList) {
                    typedBase = base;
                } else {
                    typedBase = base.asList();
                }
            } else {
                if (base instanceof RList) {
                    typedBase = base;
                    listValue = value.asList();
                    typedValue = null;
                } else if (base instanceof RRaw) {
                    if (value instanceof RRaw) {
                        typedBase = base;
                        typedValue = value.asRaw();
                    } else {
                        throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                    }
                } else if (value instanceof RRaw) {
                    throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                } else if (base instanceof RString || value instanceof RString) {
                    typedBase = base.asString();
                    typedValue = value.asString();
                } else if (base instanceof RComplex || value instanceof RComplex) {
                    typedBase = base.asComplex();
                    typedValue = value.asComplex();
                } else if (base instanceof RDouble || value instanceof RDouble) {
                    typedBase = base.asDouble();
                    typedValue = value.asDouble();
                } else if (base instanceof RInt || value instanceof RInt) {
                    typedBase = base.asInt();
                    typedValue = value.asInt();
                } else {
                    assert Utils.check(base instanceof RLogical || base instanceof RNull);
                    assert Utils.check(value instanceof RLogical);
                    typedBase = base.asLogical();
                    typedValue = (RLogical) value;
                }
            }
            int bsize = base.size();
            int vsize = typedValue != null ? typedValue.size() : listValue.size();

            // note that the base can have duplicate names, even though these cannot be created using update vector (like here),
            // they can be created e.g. using names<- or with the c() builtin

            Names bnames = base.names();
            RSymbol[] bsymbols;
            HashMap<RSymbol, Integer> nmap;
            if (bnames == null) {
                nmap = new HashMap<>(bsize);
                bsymbols = null;
            } else {
                assert Utils.check(Names.keepsMap()); // FIXME: re-visit this if we re-introduce names that don't carry hashmaps
                                                      // (probably should build a new one in such a case)
                nmap = new HashMap<>(bnames.getMap());
                bsymbols = bnames.sequence();
            }

            RSymbol[] addSymbols = new RSymbol[isize];
            int j = 0;
            // NOTE: targetOffsets is here to avoid double lookup via the hashmap
            // NOTE: firstOverwrite is here to make targetOffsets smaller in the quite common case that no (or little) new symbols are added
            int firstOverwrite = -1;
            int noverwrites = 0;
            int[] targetOffsets = null;
            for (int i = 0; i < isize; i++) {
                RSymbol name = RSymbol.getSymbol(index.getString(i));
                if (name == RSymbol.EMPTY_SYMBOL || name == RSymbol.NA_SYMBOL) { // these should never go to the map
                    addSymbols[j] = name;
                    if (targetOffsets != null) {
                        targetOffsets[i - firstOverwrite] = j + bsize;
                    }
                    j++;
                } else {
                    Integer prevOffset = nmap.get(name);
                    if (prevOffset == null) {
                        nmap.put(name, j + bsize);
                        addSymbols[j] = name;
                        if (targetOffsets != null) {
                            targetOffsets[i - firstOverwrite] = j + bsize;
                        }
                        j++;
                    } else {
                        if (firstOverwrite == -1) {
                            firstOverwrite = i;
                            targetOffsets = new int[isize - firstOverwrite];
                        }
                        noverwrites++;
                        targetOffsets[i - firstOverwrite] = prevOffset.intValue();
                    }
                }
            }

            int addSize = isize - noverwrites;
            int nsize = bsize + addSize;

            Names nnames;
            if (addSize == 0) {
                nnames = bnames;
            } else if (bsize == 0) {
                nnames = Names.create(addSymbols, nmap);
            } else {
                RSymbol[] nsymbols = new RSymbol[nsize];
                if (bsymbols != null) {
                    System.arraycopy(bsymbols, 0, nsymbols, 0, bsize);
                } else {
                    Arrays.fill(nsymbols, RSymbol.EMPTY_SYMBOL);
                }
                System.arraycopy(addSymbols, 0, nsymbols, bsize, addSize);
                nnames = Names.create(nsymbols, nmap);
            }
            RArray res = Utils.createArray(typedBase, nsize, null, nnames, base.attributesRef());
            for (int bi = 0; bi < bsize; bi++) {
                res.set(bi, typedBase.get(bi));
            }

            int vi = 0;
            int ii = 0;
            if (noverwrites == 0) {
                for (; ii < isize; ii++) {
                    // TODO will the ? check be lifted out of the loop, or
                    // should it be done explicitly
                    res.set(bsize + ii, typedValue != null ? typedValue.get(vi) : listValue.get(vi));
                    vi++;
                    if (vi == vsize) {
                        vi = 0;
                    }
                }
            } else {
                // some overwrites (either update of an existing field, or a duplicate name in the update vector)
                for (; ii < isize; ii++) {
                    int ni;
                    if (ii < firstOverwrite) {
                        ni = bsize + ii;
                    } else {
                        ni = targetOffsets[ii - firstOverwrite];
                    }
                    // TODO will the ? check be lifted out of the loop, or should it be done explicitly
                    res.set(ni, typedValue != null ? typedValue.get(vi) : listValue.get(vi));
                    vi++;
                    if (vi == vsize) {
                        vi = 0;
                    }
                }
            }
            if (vi != 0) {
                RContext.warning(ast, RError.NOT_MULTIPLE_REPLACEMENT);
            }
            return res;
        }
    }

    // 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 UpdateVector {
        public Subscript(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
            assert Utils.check(!subset);
        }

        public static RAny executeSubscript(RInt index, RArray base, RArray value, ASTNode ast) {
            // FIXME: check handling of dimensions here
            final int isize = index.size();
            if (isize == 0) { throw RError.getSelectLessThanOne(ast); }
            int i = 0;
            RAny b = base;
            RAny res = null;
            RList parent = null;
            int parentIndex = -1;
            boolean onSharedPath = false;
            if (isize > 1) {
                for (; i < isize - 1; i++) { // shallow copy
                    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 = ReadVector.Subscript.convertDereferencingIndex(indexv, i, bsize, ast);

                    if (l.isShared()) {
                        onSharedPath = true;
                    }
                    if (onSharedPath) {
                        RAny[] content = new RAny[bsize];
                        int k = 0;
                        int j = 0;
                        for (; j < isel; j++) { // shallow copy
                            content[k++] = l.getRAnyRef(j);
                        }
                        j++; // skip
                        k++;
                        for (; j < bsize; j++) { // shallow copy
                            content[k++] = l.getRAnyRef(j);
                        }
                        RList newList = RList.RListFactory.getFor(content, l.dimensions(), l.names(), l.attributesRef());
                        // FIXME: this copy can be unnecessary
                        if (parent != null) {
                            parent.set(parentIndex, newList);
                        } else {
                            res = newList;
                        }
                        parent = newList;
                        b = l.getRAnyRef(isel); // shallow copy
                    } else {
                        if (parent == null) {
                            res = l;
                        }
                        parent = l;
                        b = l.getRAny(isel); // shallow copy
                    }
                    parentIndex = isel;

                }
            }
            // selection at the last level
            int indexv = index.getInt(i);
            if (!(b instanceof RArray)) { throw RError.getObjectNotSubsettable(ast, b.typeOf()); }
            RArray a = (RArray) b;
            if (value instanceof RNull) {
                if (a instanceof RList) {
                    b = GenericScalarSelection.deleteElement((RList) a, indexv, ast, false);
                } else {
                    throw RError.getMoreElementsSupplied(ast);
                }
            } else {
                if (value.size() > 1) {
                    throw RError.getMoreElementsSupplied(ast);
                } else {
                    b = ScalarNumericSelection.genericUpdate(a, indexv, value, false, ast);
                }
            }
            if (parent == null) {
                return b;
            } else {
                parent.set(parentIndex, b);
                return res;
            }
        }

        public static RAny executeSubscript(RString index, RArray base, RArray value, ASTNode ast) {
            final int isize = index.size();
            if (isize == 0) { throw RError.getSelectLessThanOne(ast); }
            int i = 0;
            RAny b = base;
            RAny res = null;
            RList parent = null;
            int parentIndex = -1;
            if (isize > 1) {
                for (; i < isize - 1; i++) { // shallow copy
                    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); }
                    int bsize = l.size();
                    RAny[] content = new RAny[bsize];
                    // TODO: add optimization like in the case with integer index (above)
                    int k = 0;
                    int j = 0;
                    for (; j < indexv; j++) { // shallow copy
                        content[k++] = l.getRAny(j);
                    }
                    j++; // skip
                    k++;
                    for (; j < bsize; j++) { // shallow copy
                        content[k++] = l.getRAny(j);
                    }
                    RList newList = RList.RListFactory.getFor(content, l.dimensions(), l.names(), l.attributesRef());
                    if (parent != null) {
                        parent.set(parentIndex, newList);
                    } else {
                        res = newList;
                    }
                    parent = newList;
                    parentIndex = indexv;
                    b = l.getRAnyRef(indexv); // shallow copy
                }
            }
            // selection at the last level
            if (!(b instanceof RArray)) { throw RError.getObjectNotSubsettable(ast, b.typeOf()); }
            RArray a = (RArray) b;
            if (value instanceof RNull) {
                if (a instanceof RList) {
                    b = GenericScalarSelection.deleteElement((RList) a, index.getString(i));
                    // TODO: call directly a method for string index
                } else {
                    throw RError.getMoreElementsSupplied(ast);
                }
            } else {
                if (value.size() > 1) {
                    throw RError.getMoreElementsSupplied(ast);
                } else {
                    b = ScalarStringSelection.genericUpdate(a, index.getString(i), value, false, ast);
                    // FIXME: ScalarNumericSelection.genericUpdate is unnecessarily heavy-weight for a valid positive index
                }
            }
            if (parent == null) {
                return b;
            } else {
                parent.set(parentIndex, b);
                return res;
            }
        }

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

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing Subscript");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                RArray avalue = (RArray) value;
                return executeSubscript(index, abase, avalue, ast);

            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_UP) Utils.debug("update - Subscript failed: " + f);
                GenericSelection gs = new GenericSelection(ast, isSuper, var, lhs, indexes, rhs, subset);
                // rewriting itself only to handle the error, there is no way to recover
                replace(gs, "install GenericSelection from Subscript");
                if (DEBUG_UP) Utils.debug("update - replaced and re-executing with GenericSelection");
                return gs.execute(base, index, value);
            }
        }
    }

    // handles any update, won't rewrite itself
    public static class GenericSelection extends UpdateVector {

        public GenericSelection(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RNode[] indexes, RNode rhs, boolean subset) {
            super(ast, isSuper, var, lhs, indexes, rhs, subset);
        }

        @Override public RAny execute(RAny base, RAny index, RAny value) {
            if (DEBUG_UP) Utils.debug("update - executing GenericSelection");
            try {
                if (!(base instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_BASE); }
                RArray abase = (RArray) base;
                if (!(index instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_INDEX); }
                assert Utils.check(subset);
                RArray aindex = (RArray) index;
                int isize = aindex.size();
                if (value instanceof RNull) {
                    if (isize == 1) {
                        return GenericScalarSelection.deleteElement(abase, aindex, ast, subset);
                    } else {
                        if (!(abase instanceof RList)) { throw RError.getReplacementZero(ast); }
                        RList lbase = (RList) abase;
                        if (aindex instanceof RDouble || aindex instanceof RInt) {
                            return NumericSelection.deleteElements(lbase, aindex.asInt(), ast, true);
                        } else if (aindex instanceof RLogical) {
                            return LogicalSelection.deleteElements(lbase, index.asLogical(), ast);
                        } else if (aindex instanceof RString) {
                            return StringSelection.deleteElements(lbase, (RString) index, ast);
                        } else if (aindex instanceof RNull) {
                            return lbase;
                        } else {
                            throw RError.getInvalidSubscriptType(ast, aindex.typeOf());
                        }
                    }
                }
                // TODO: allow storing non-array values into lists (e.g. closures)
                if (!(value instanceof RArray)) { throw new SpecializationException(Failure.NOT_ARRAY_VALUE); }
                RArray avalue = (RArray) value;

                if (aindex instanceof RDouble || aindex instanceof RInt) {
                    return NumericSelection.genericUpdate(abase, aindex.asInt(), avalue, ast, true);
                } else if (aindex instanceof RLogical) {
                    return LogicalSelection.genericUpdate(abase, index.asLogical(), avalue, ast);
                } else if (aindex instanceof RString) {
                    return StringSelection.genericUpdate(abase, (RString) index, avalue, ast);
                } else {
                    throw RError.getInvalidSubscriptType(ast, aindex.typeOf());
                }
            } catch (SpecializationException e) {
                Failure f = (Failure) e.getResult();
                if (DEBUG_UP) Utils.debug("update - GenericSelection failed: " + f);
                switch (f) {
                case NOT_ARRAY_BASE:
                    throw RError.getObjectNotSubsettable(ast, base.typeOf());
                case NOT_ARRAY_INDEX:
                    throw RError.getInvalidSubscriptType(ast, index.typeOf());
                default:
                    assert Utils.check(f == Failure.NOT_ARRAY_VALUE);
                    throw RError.getSubassignTypeFix(ast, value.typeOf(), base.typeOf());
                }
            }
        }
    }

    // Dollar selection operator
    // ---------------------------------------------------------------------------------------

    /**
     * Base class for all dollar selection ($) assignments. Defines final methods for updates, updates in place and
     * appends of lists, typecasts to lists, position checking, etc. This class does not define the execute method and
     * only acts as a common codebase for its descendants, where the DollarListUpdate is the root of the hierarchy.
     */

    // TODO: support recursive indexing
    public abstract static class DollarUpdateBase extends UpdateVector {

        RSymbol index;

        DollarUpdateBase(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RSymbol index, RNode rhs) {
            super(ast, isSuper, var, lhs, new RNode[]{new BaseR(null) {

                @Override public Object execute(Frame frame) {
                    return null; // never used, but must be here as the base of update vector always evaluates the index
                }

            }}, rhs, false);
            this.index = index;
        }

        DollarUpdateBase(DollarUpdateBase from) {
            super(from);
            this.index = from.index;
        }

        /**
         * Converts the given base to list, emitting the warning about coercion for R compatibility.
         */
        protected final RList convertToList(RAny base) {
            RContext.warning(ast, RError.COERCING_LHS_TO_LIST);
            return base.asList();
        }

        /**
         * Returns the position of the given symbol in the specified array names, or -1 if no such name exists in the
         * array.
         */
        protected static int elementPos(RArray.Names names, RSymbol idx) {
            return (names == null) ? -1 : names.map(idx);
        }

        /**
         * Appends the given value to the list under specified name.
         */
        protected static RAny appendToList(RArray base, RArray.Names names, int size, RAny value, RSymbol idx) {
            // if names not empty, create them
            RArray.Names myNames = names;
            if (myNames == null) {
                myNames = RArray.Names.create(size);
            }
            RArray res = Utils.createArray(base, size + 1, base.dimensions(), UpdateVector.appendName(myNames, idx), base.attributesRef());
            for (int i = 0; i < size; ++i) {
                res.set(i, base.get(i));
            }
            res.set(size, value);
            return res;
        }

        /**
         * Creates a copy of the given list and then updates the specified position in it.
         */
        protected static RAny updateList(RArray base, RArray.Names names, int size, RAny value, int pos) {
            RArray res = Utils.createArray(base, size, base.dimensions(), names, base.attributesRef());
            for (int i = 0; i < pos; ++i) {
                res.set(i, base.get(i));
            }
            for (int i = pos + 1; i < size; ++i) {
                res.set(i, base.get(i));
            }
            return res.set(pos, value);
        }

        /**
         * Updates the given list in place - its specified position is rewritten to the supplied value and the same list
         * is returned.
         */
        protected static RAny updateListInPlace(RArray base, RAny value, int pos) {
            return base.set(pos, value);
        }
    }

    /**
     * Fast update of a non shared list. This class assumes that it has (a) a list, (b) it must update the list, not
     * append to it, and (c) the list is not shared, in which case performs the operation. Otherwise rewrites itself to
     * either DollarUpdate if not a list, DollarSharedListUpdate if not shared update and DollarListAppend.
     */
    public static class DollarListUpdate extends DollarUpdateBase {

        static enum Failure {
            NOT_A_LIST, SHARED_UPDATE, NOT_AN_UPDATE,
        }

        public DollarListUpdate(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RSymbol index, RNode rhs) {
            super(ast, isSuper, var, lhs, index, rhs);
        }

        public DollarListUpdate(DollarUpdateBase from) {
            super(from);
        }

        /**
         * Performs in place update of a list, or rewrites itself to the appropriate nodes.
         */
        @Override RAny execute(RAny base, RAny indexDummy, RAny value) {
            try {
                if (!(base instanceof RList)) { throw new SpecializationException(Failure.NOT_A_LIST); }
                if (value instanceof RNull) { throw new SpecializationException(Failure.NOT_AN_UPDATE); }
                RList list = (RList) base;
                RArray.Names names = list.names();
                int pos = elementPos(names, index);
                if (pos == -1) { throw new SpecializationException(Failure.NOT_AN_UPDATE); }
                if (list.isShared()) { throw new SpecializationException(Failure.SHARED_UPDATE); }
                return updateListInPlace(list, value, pos);
            } catch (SpecializationException e) {
                DollarUpdateBase x;
                switch ((Failure) e.getResult()) {
                case NOT_A_LIST:
                    x = new DollarUpdate(this);
                    replace(x, "not a list in assignment");
                    return x.execute(base, index, value);
                case SHARED_UPDATE:
                    x = new DollarSharedListUpdate(this);
                    replace(x, "update of a shared list");
                    return x.execute(base, index, value);
                case NOT_AN_UPDATE:
                    x = new DollarListAppend(this);
                    replace(x, "list append");
                    return x.execute(base, index, value);
                }
            }
            assert Utils.check(false);
            return null;
        }
    }

    /**
     * Performs an update of a shared list, or an update of a non-shared list without rewriting itself, rewrites to
     * append instead of update, or to perform the general operation with coercion to list.
     */

    // TODO: extract the constant (symbol) used for the selection statically!
    public static class DollarSharedListUpdate extends DollarUpdateBase {

        static enum Failure {
            NOT_A_LIST, NOT_AN_UPDATE,
        }

        public DollarSharedListUpdate(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RSymbol index, RNode rhs) {
            super(ast, isSuper, var, lhs, index, rhs);
        }

        public DollarSharedListUpdate(DollarUpdateBase from) {
            super(from);
        }

        /**
         * Updates shared list while first copying it, or non-shared list in place. Rewrites to general case or to
         * append instead of update.
         */
        @Override RAny execute(RAny base, RAny indexDummy, RAny value) {
            try {
                if (!(base instanceof RList)) { throw new SpecializationException(Failure.NOT_A_LIST); }
                if (value instanceof RNull) { throw new SpecializationException(Failure.NOT_AN_UPDATE); }
                RList list = (RList) base;
                RArray.Names names = list.names();
                int size = list.size();
                int pos = elementPos(names, index);
                if (pos == -1) { throw new SpecializationException(Failure.NOT_AN_UPDATE); }
                if (list.isShared()) {
                    return updateList(list, names, size, value, pos);
                } else {
                    return updateListInPlace(list, value, pos);
                }
            } catch (SpecializationException e) {
                DollarUpdateBase x;
                switch ((Failure) e.getResult()) {
                case NOT_A_LIST:
                    x = new DollarUpdate(this);
                    replace(x, "not a list in assignment");
                    return x.execute(base, index, value);
                case NOT_AN_UPDATE:
                    x = new DollarListAppend(this);
                    replace(x, "list append");
                    return x.execute(base, index, value);
                }
            }
            assert (false);
            return null;
        }
    }

    /**
     * Appends given list (shared or non shared). If not append, or not a list rewrites to the general case
     * DollarUpdate.
     */
    public static class DollarListAppend extends DollarUpdateBase {

        public DollarListAppend(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RSymbol index, RNode rhs) {
            super(ast, isSuper, var, lhs, index, rhs);
        }

        public DollarListAppend(DollarUpdateBase from) {
            super(from);
        }

        /**
         * Performs the update or overwrites itself to the general case.
         */
        @Override RAny execute(RAny base, RAny indexDummy, RAny value) {
            try {
                if (!(base instanceof RList) || value instanceof RNull) { throw new SpecializationException(null); }
                RList list = (RList) base;
                RArray.Names names = list.names();
                int size = list.size();
                int pos = elementPos(names, index);
                if (pos != -1) { throw new SpecializationException(null); }
                return appendToList(list, names, size, value, index);
            } catch (SpecializationException e) {
                DollarUpdateBase x = new DollarUpdate(this);
                replace(x, "not a list or not append in assignment");
                return x.execute(base, index, value);
            }
        }
    }

    /**
     * General update/append on a list/vector. Coerces the input type to a list if required.
     */
    public static class DollarUpdate extends DollarUpdateBase {

        public DollarUpdate(ASTNode ast, boolean isSuper, RSymbol var, RNode lhs, RSymbol index, RNode rhs) {
            super(ast, isSuper, var, lhs, index, rhs);
        }

        public DollarUpdate(DollarUpdateBase from) {
            super(from);
        }

        // / TODO Are the specializations for the fast stuff worth it? This code
        // looks smaller than the code with many rewrite possibilities
        @Override RAny execute(RAny base, RAny indexDummy, RAny value) {
            RArray list = (base instanceof RList) ? (RList) base : convertToList(base);
            RArray.Names names = list.names();
            int size = list.size();
            int pos = elementPos(names, index);
            if (value instanceof RNull) {
                if (pos != -1) {
                    return GenericScalarSelection.deleteElement((RList) list, pos, list.size());
                } else {
                    return base;
                }
            }
            if (pos == -1) {
                return appendToList(list, names, size, value, index);
            } else {
                if (base.isShared()) {
                    return updateList(list, names, size, value, pos);
                } else {
                    return updateListInPlace(list, value, pos);
                }
            }
        }
    }
}
TOP

Related Classes of r.nodes.exec.UpdateVector$DoubleBaseSimpleSelection$ScalarIntSelection

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.