// Operator overloading to make a 1-based array
class Arr {
    protected var value;
    public proc create() {
        if (oadl::arg(0).isarray()) {
            // If an array is passed as the first arg, make
            // it the initializer
            if ((oadl::nargs() < 2) || !oadl::arg(1)) {
                // By-reference unless "false" is the second arg
                value = oadl::arg(0);
            }
            else {
                // By-value otherwise
                value = @oadl::arg(0);
            }
        }
        else if (oadl::nargs()) {
            // Otherwise, assume it is as if it were an iterator
            value = new Array(oadl::argvec().pack());
            forall (value#[i]) value#[i] = i+1;
        }
        else {
            value = nil;
        }
    }
    operator [] () { // a[i,j,k,...] => [](i,j,k,...)
        var i, n = oadl::nargs(), res = value;
        for (i = 0; i < n; i++) {
            res = res[oadl::arg(i)-1];
        }
        return res;
    }
    operator [=] () { // a[i,j,k,...] = b => [=](b,i,j,k,...)
        var n = oadl::nargs();
        var val = oadl::arg(n-1);
        var idx = oadl::argvec()[:n-2] - 1;
        var hash = (idx*value.stride()).reduce(`+);
        value#[hash] = val;
    }
    operator #[] (idx) {
        return value#[idx-1];
    }
    operator #[=] (idx,val) {
        value#[idx-1] = val;
    }
    operator + (rhs) {
        if (rhs ?= Arr) rhs = rhs.value;
        return new Arr(value + rhs);
    }
    operator \+ (lhs) {
        return new Arr(lhs + value);
    }
    operator - (rhs) {
        if (rhs ?= Arr) rhs = rhs.value;
        return new Arr(value - rhs);
    }
    operator \- (lhs) {
        return new Arr(lhs + value);
    }
    operator !- (lhs) {
        return new Arr(-value);
    }
    operator * (rhs) {
        if (rhs ?= Arr) rhs = rhs.value;
        return new Arr(value * rhs);
    }
    operator \* (lhs) {
        return new Arr(lhs * value);
    }
    operator / (rhs) {
        if (rhs ?= Arr) rhs = rhs.value;
        return new Arr(value / rhs);
    }
    operator \/ (lhs) {
        return new Arr(lhs / value);
    }
    public proc print() {
        "", value, '\n';
    }
    public proc sizeof() { return value.sizeof(); }
    public proc shape() { return value.shape(); }
    public proc rank() { return value.rank(); }
    public proc length() { return value.length(); }
    public proc isarray() { return true; }
    public proc concat(b) { return new Arr(value ## b); }
    public proc subr() {
        var a = oadl::argvec();
        forall (a[i]) if (a[i] != nil) a[i]--;
        a = value.subr#(a);
        return new Arr(a, false);
    }
}

proc main()
{
    var a = new Arr(2,3,4);

    "a[1,1,1] = ", a[1,1,1], '\n';
    forall (a[i,j,k]) a[i+1,j+1,k+1] *= 100;
    forall (a#[i]) a#[i+1] -= 1;
    a.print();
    a += 1;
    a /= 100;
    a.print();
    var b = a[1:2,1:2,1:2];
    b[1,1,1] = 1000;
    b.print();
    a.print();
    b = foreach (a[i,j,k]) {
            "a[" ## String(i+1) ## ","
                 ## String(j+1) ## ","
                 ## String(k+1) ## "] = "
                 ## String(a[i+1,j+1,k+1])
        };
    "", b, '\n';
}