# Copyright 2006 James Tauber and contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# iteration from Bob Ippolito's Iteration in JavaScript

# must declare import _before_ importing sys

from __pyjamas__ import INT, JS, setCompilerOptions, debugger

setCompilerOptions("noDebug", "noBoundMethods", "noDescriptors", "noAttributeChecking", "noSourceTracking", "noLineTracking", "noStoreSource")

# Patching of the standard javascript Array object
# This makes it imposible to use for (k in Array())
JS("""
if (typeof Array.prototype.indexOf != 'function') {
    Array.prototype.indexOf = function(elt /*, from*/) {
        var len = this.length >>> 0;

        var from = Number(arguments[1]) || 0;
        from = (from < 0)
                ? Math.ceil(from)
                : Math.floor(from);
        if (from < 0)
            from += len;

        for (; from < len; from++) {
            if (from in this &&
                this[from] === elt)
                return from;
        }
        return -1;
    };
};
""")

# Patching of the standard javascript RegExp
JS("""
RegExp.prototype.Exec = function(pat) {
    var m = this.exec(pat);
    if (m !== null) {
        var len = m.length >>> 0;
        for (var i = 0; i < len; i++) {
            if (typeof(m[i]) == "undefined")
                m[i] = null;
        }
    }
    return m;
};
""")

JS("""
@{{platform}} = $pyjs.platform;
@{{sys}} = null;
@{{dynamic}} = null;

var $max_float_int = 1;
for (var i = 0; i < 1000; i++) {
    $max_float_int *= 2;
    if ($max_float_int + 1 == $max_float_int) {
        break;
    }
}
var $max_int = 0x7fffffff;
var $min_int = -0x80000000;

var $radix_regex = [
    /^$/i,                  //  0
    /^a/i,                  //  1
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[01]+ *$/i,     //  2
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-2]+ *$/i,    //  3
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-3]+ *$/i,    //  4
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-4]+ *$/i,    //  5
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-5]+ *$/i,    //  6
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-6]+ *$/i,    //  7
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-7]+ *$/i,    //  8
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-8]+ *$/i,    //  9
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9]+ *$/i,    // 10
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a]+ *$/i,   // 11
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9ab]+ *$/i,  // 12
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-c]+ *$/i, // 13
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-d]+ *$/i, // 14
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-e]+ *$/i, // 15
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-f]+ *$/i, // 16
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-g]+ *$/i, // 17
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-h]+ *$/i, // 18
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-i]+ *$/i, // 19
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-j]+ *$/i, // 20
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-k]+ *$/i, // 21
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-l]+ *$/i, // 22
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-m]+ *$/i, // 23
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-n]+ *$/i, // 24
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-o]+ *$/i, // 25
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-p]+ *$/i, // 26
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-q]+ *$/i, // 27
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-r]+ *$/i, // 28
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-s]+ *$/i, // 29
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-t]+ *$/i, // 30
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-u]+ *$/i, // 31
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-v]+ *$/i, // 32
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-w]+ *$/i, // 33
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-x]+ *$/i, // 34
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-y]+ *$/i, // 35
    /^[\\t\\n\\x0b\\x0c\\r ]*-? *[0-9a-z]+ *$/i  // 36
];


/**
 * create the __mro__ for a class
 */
function mro_merge(seqs) {
    var res = new Array();
    var i = 0;
    var cand = null;
    function resolve_error(candidates) {
        @{{raise}}($new(@{{TypeError}}, B$str("Cannot create a consistent method resolution order (MRO) for bases " + candidates[0].__name__ + ", "+ candidates[1].__name__)));
    }
    for (;;) {
        var nonemptyseqs = new Array();
        for (var j = 0; j < seqs.length; j++) {
            if (seqs[j].length > 0) nonemptyseqs.push(seqs[j]);
        }
        if (nonemptyseqs.length == 0) return res;
        i++;
        var candidates = new Array();
        for (var j = 0; j < nonemptyseqs.length; j++) {
            cand = nonemptyseqs[j][0];
            candidates.push(cand);
            var nothead = new Array();
            for (var k = 0; k < nonemptyseqs.length; k++) {
                for (var m = 1; m < nonemptyseqs[k].length; m++) {
                    if (cand === nonemptyseqs[k][m]) {
                        nothead.push(nonemptyseqs[k]);
                    }
                }
            }
            if (nothead.length != 0)
                cand = null; // reject candidate
            else
                break;
        }
        if (cand == null) {
            resolve_error(candidates);
        }
        res.push(cand);
        for (var j = 0; j < nonemptyseqs.length; j++) {
            if (nonemptyseqs[j][0] === cand) {
                nonemptyseqs[j].shift();
            }
        }
    }
}

/**
 * Initial type function
 * will be used before definition of tuple type
 *
 * clsname : javascript string with class name
 * bases: javascript array with base classes
 * dict: javascript object
 * module: module object (i.e. namespace)
 * Note
 */
function tmp_type(module, clsname, bases, dict) {
    var cls, mro = new Array();
    if (module !== ${0,module}$) {
        debugger;
    }
    cls = function () {
        var args = Array.prototype.slice.call(arguments);
        if (args.length >= 3) {
            var len = args.length;
            if ((args[len-3] === null || args[len-3]['__class__'] === @{{tuple}}) &&
                (args[len-2] === null || args[len-3]['__class__'] === @{{dict}}) &&
                (args[len-1] === null || typeof args[len-1]['__class__'] == "undefined")) {
                return $newext.apply(this, [arguments.callee].concat(args));
            }
        }
        return $new.apply(this, [arguments.callee].concat(args));
};
    cls['$inst'] = false;
    cls['__name__'] = clsname;
    cls['__bases__'] = bases;
    for (var i = 0; i < bases.length; i++) {
        mro.push(new Array().concat(bases[i].__mro__));
    }
    cls['__mro__'] = [cls].concat(mro_merge(mro));
    cls['$dict'] = {};
    var __module__ = typeof module['$dict'] != "undefined" ? module['$dict']['__name__'] : module['__name__'];
    if (typeof __module__ != "undefined") {
        cls['$dict']['__module__'] = __module__;
    }
    cls['__slots__'] = [];
    cls['__dict__'] = {
        '__object': cls['$dict']
    };
    cls.toString = function () { return "<class " + clsname + ">";};
    return cls;
}

/**
 * builtin type function
 *
 * clsname : javascript string with class name
 * bases: tuple with base classes
 * dict: proxydict instance
 * module: module object (i.e. namespace)
 */
function type(module, clsname, bases, dict, slots) {
${1, type_class, cls, module, clsname, bases, dict}$
    if (typeof slots != "undefined") {
        cls['__slots__'] = slots;
    }
    cls.toString = function () { return "<class " + clsname + ">";};
    return cls;
}
@{{B$type}} = type;

${0, fcall}$ = @{{fcall}} = function (module, lineno, obj, base /*, args*/) {
    var method = null;
${1, call_head, 4}$
${1, call_object}$
${1, call_tail}$
};

${0, fcallext}$ = @{{fcallext}} = function (module, lineno, obj, base /*, args, star_args, dstar_args, named_args*/) {
    var method = null;
${1, call_head, 4}$
${1, call_object}$
${1, call_star_dstar}$
${1, call_tail}$
};

${0, mcall}$ = @{{mcall}} = function (module, lineno, obj, method /*, args*/) {
    var base = null;
${1, call_head, 4}$
${1, call_method}$
${1, call_object}$
${1, call_tail}$
};

${0, mcallext}$ = @{{mcallext}} = function (module, lineno, obj, method /*, args, star_args, dstar_args, named_args*/) {
    var base = null;
${1, call_head, 4}$
${1, call_method}$
${1, call_object}$
${1, call_star_dstar}$
${1, call_tail}$
};

var func = @{{func}} = function (module, lineno, obj, name, type, args, starargs, dstarargs, defaults, def, tobase) {
    // obj is class or local scope
    var o, new_def = def;
    def['__name__'] = B$str(name);
    def['__module__'] = module['__name__'];
    def['func_defaults'] = defaults;
    // non-python func_...
    if (args === null) args = [];
    def['func_args'] = args;
    def['func_starargs'] = starargs;
    def['func_dstarargs'] = dstarargs;
    def['func_minargs'] = (defaults === null? args.length : args.length - defaults.length);
    def['func_type'] = type;
    def['_module'] = module;
    def['_lineno'] = lineno;
    switch (type) {
        case ${0, jsmethod}$:
            // javascript
            break;
        case ${0, staticmethod}$:
            // staticmethod
            new_def = B$__new__(@{{staticmethod}});
            new_def['im_func'] = def;
            // continue to patch the function
        case ${0, function}$:
            // function
            def['$inst'] = true;
            def['__class__'] = @{{function}};
            def['__slots__'] = [];
            break;
        case ${0, classmethod}$:
            // classmethod
            new_def = B$__new__(@{{classmethod}});
            new_def['im_func'] = def;
            new_def['im_class'] = obj;
            break;
        case ${0, wrappermethod}$:
            // slot wrapper look alike
            // is implemented as a instance method, but can have
            // any type as first argument
            new_def = $new(@{{wrappermethod}}, def, null, obj);
            obj[name] = def;
            return;
            break;
        default:
            // error
            throw "Translator error. Unknown function creation type.";
            break;
    }
    if (typeof obj['$dict'] == "undefined" || tobase === true) {
        obj[name] = new_def;
    } else {
        obj['$dict'][name] = new_def;
    }
    return new_def;
}

var $new = ${0, module}$['$new'] = @{{$new}} = function (cls/*, args*/) {
    var args = Array.prototype.slice.call(arguments, 1);
${1, create_instance, args, cls, @{{mcall}}, @{{fcall}}}$
}

//var $newext = @{{$newext}} = function ($cls/*, args, star_args, dstar_args, named_args*/) {
// function ($cls/*, args, star_args, dstar_args, named_args*/)
var $newext = @{{$newext}} = func(${0,module}$, null, {}, '__call__', ${0, jsmethod}$, ['cls'], null, null, null, function ($cls) {
    var args, cls = $cls;
    if (arguments.length >= 4) {
        args = Array.prototype.slice.call(arguments, 1);
    } else {
        args = Array.prototype.slice.call(arguments, 0);
        cls = args[0].__array[0];
        args[0].__array.shift();
    }
${1, create_instance, args, cls, @{{mcallext}}, @{{fcallext}}}$
});

// Some short-cut functions for specific instances
function B$bool($v) {
${1, new_bool, $v, @{{True}}, @{{False}}}$
};
${0, bool}$ = @{{B$bool}} = B$bool;

function B$booljs($v) {
    if ($v === null) return false;
${1, new_bool, $v, true, false}$
};
${0, booljs}$ = @{{B$booljs}} = B$booljs;

function B$dict($v, ignore_undef) {
${1, __new__, instance, @{{dict}}, false}$
    instance['__object'] = {};
    instance['__object_hash'] = {};
    instance['__object_head'] = null;
    instance['__object_tail'] = null;
    instance['_length'] = 0;
    instance['valueOf'] = function() {return this.__object;};
    instance['getObject'] = function() {return this.__object;}; // backwards compatability TODO: remove this method
    instance['toString'] = function() {return this.__object.toString();};
    if (typeof $v != "undefined") {
        var h, k, v, node, head, tail, obj = instance['__object'], objH = instance['__object_hash'];
        head = tail = {'prev': null, 'next': null, 'key': null}
        for (k in $v) {
            v = $v[k];
            if (typeof k != "undefined" && typeof v != "undefined") {
                h = @{{hash}}(k);
                tail.next = node = {'prev': tail, 'next': null, 'key': k}
                tail = node;
                objH[h] = node;
                obj[h] = v;
                instance._length ++;
            } else {
                if (ignore_undef !== true) {
                    if (typeof k == "undefined") {
                        @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (key is undefined)")));
                    }
                    @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (value for '" + k + "' is undefined)")));
                }
            }
        }
        if (head !== tail) {
            instance.__object_head = head.next;
            instance.__object_head.prev = null;
            instance.__object_tail = tail;
        }
    }
    return instance;
};

function B$dictproxy($v) {
    if (typeof $v == "undefined") $v = {};
${1, __new__, instance, @{{dictproxy}}, false}$
    instance['__object'] = $v;
    instance['valueOf'] = function() {return this.__object;};
    instance['toString'] = function() {return this.__object.toString();};
    return instance;
};

function B$list($v) {
    if (typeof $v == "undefined") $v = [];
${1, __new__, instance, @{{list}}, false}$
    instance['__array'] = $v;
    instance['valueOf'] = function() {return this.__array;};
    instance['toString'] = function() {return this.__array.toString();};
    return instance;
};
@{{B$list}} = B$list;

function B$str($v) {
    if (typeof $v == "undefined") $v = "";
${1, __new__, instance, @{{str}}, false}$
    instance['__s'] =  $v;
    instance['valueOf'] =  function() {return this.__s;};
    instance['toString'] =  function() {return this.__s.toString();};
    return instance;
};
@{{B$str}} = B$str;

function B$tuple($v) {
    if (typeof $v == "undefined") $v = [];
${1, __new__, instance, @{{tuple}}, false}$
    instance['__array'] =  $v;
    instance['valueOf'] =  function() {return this.__array;};
    instance['toString'] =  function() {return this['__class__']['$dict']['__str__'](this).valueOf();};
    return instance;
};
@{{B$tuple}} = B$tuple;


/*
To create  valid type, we depend on the existance of:
 - object type (which is the base in all __mro__
 - tuple type (to create __bases__)
 - dictproxy type (to create the class __dict__)
 - the type type (is always in the class __class__)
 - the module type (is used in the type creation)
 - the basestring and str type (needed for class __name__)
 - the classmethod type (is used for the class __call__)
Not that in python the method-wrapper class is used for
class.__call__ (but seems to do the same as classmethod)

Therefore we'll do:
 - Create object type with tmp_type
 - Create tuple type with tmp_type
 - Create dictproxy type with tmp_type
 - Create type type with tmp_type
 - Create module type with tmp_type
 - Create basestring type with tmp_type
 - Create str type with tmp_type
 - Create classmethod type with tmp_type

Since we have the B$tuple and the B$str function, we can now:
 - Replace the __bases__ of the types with a tuple
 - Replace the __dict__ of the types with a dictproxy
 - Replace the __name__ of the types with a str
 - Add __class__ to the types
 - Add __call__ to the types
 - Remove tmp_type and use type for the rest of the code
   without implementing the complet tuple type yet.
*/

// We'll use the javascript Function as fuction class/instance
@{{function}} = Function;

@{{object}} = tmp_type(${0,module}$, 'object', [], {});
var B$__new__ = func(${0,module}$, null, @{{object}}, '__new__', ${0, jsmethod}$, ['cls'], 'args', 'kwargs', null, function($cls) {
${1, __new__, instance, $cls}$
    return instance;
});
@{{tuple}} = tmp_type(${0,module}$, 'tuple', [@{{object}}], {});
@{{dictproxy}} = tmp_type(${0,module}$, 'dictproxy', [@{{object}}], {});
@{{type}} = tmp_type(${0,module}$, 'type', [@{{object}}], {});
${0,module}$['__class__'] = @{{module}} = tmp_type(${0,module}$, 'module', [@{{object}}], {});
@{{basestring}} = tmp_type(${0,module}$, 'basestring', [@{{object}}], {});
@{{str}} = tmp_type(${0,module}$, 'str', [@{{basestring}}], {});
@{{classmethod}} = tmp_type(${0,module}$, 'classmethod', [@{{object}}], {});
delete tmp_type;

// Some basic functions (some of them are needed very early (like _getattr)

// The use of stopiter depends on the single threaded nature of javascript
@{{stopiter}} = false;
@{{noraise}} = null;
func(${0,module}$, null, ${0,g}$, 'raise', 1, ['cls', 'value', 'tb'], null, null, [null, null, null], function($cls, $value, $tb) {
    var exc = null;
    if (@{{stopiter}}) {
        @{{stopiter}} = false;
        if ($cls === @{{StopIteration}} || $cls['__class__'] === @{{StopIteration}}) {
            return @{{StopIter}};
        }
    }
    if (typeof $value == "undefined") {
        $value = null;
    }
    if (typeof $tb == "undefined") {
        $tb = null;
    }
    if (@{{noraise}} !== null) {
        if (@{{noraise}} === $cls || @{{noraise}} === $cls['__class__']) {
            $cls = @{{noraise}};
            @{{noraise}} = null;
            return $cls;
        }
    }
    if ($cls === null || typeof $cls == "undefined") {
        // re-raise last exception
    } else if ($value === null || $value.valueOf() === null) {
        if ($cls['$inst'] === true) {
            $pyjs.__last_exception__=  $cls;
        } else {
            $pyjs.__last_exception__ = $new($cls);
        }
    } else if ($cls['$inst'] === true) {
        if ($value.valueOf() === null) {
            $pyjs.__last_exception__= $cls;
        }
        $pyjs.__last_exception__ = $new(@{{TypeError}}, B$str("instance exception may not have a separate value"));
    } else if ($cls['$inst'] !== false) {
        $pyjs.__last_exception__ = $new(@{{TypeError}}, B$str("exceptions must be old-style classes or derived from BaseException"));
    } else {
        var mro;
        $pyjs.__last_exception__ = null;
        if ($value['$inst'] === true) {
            mro = $value['__class__']['__mro__'];
            for (var i = 0; i < mro.length; i++) {
                if (mro[i] === @{{tuple}}) {
                    mro = [$cls];
                    mro = mro.concat($value.__array);
                    $pyjs.__last_exception__ = inst.apply($cls, mro);
                }
            }
        }
        if ($pyjs.__last_exception__ === null) {
            $pyjs.__last_exception__ = $new($cls, $value);
        }
    }
    throw $pyjs.__last_exception__;
});

func(${0,module}$, null, ${0,g}$, 'setattr', ${0, function}$, ['obj', 'name', 'value'], null, null, null, function($obj, $name, $value) {
    var attr, __setattr__, __set__, mro, slots, retval, obj,
        attrname = typeof $name['__s'] != "undefined" ? attrname = $name['__s'] : attrname = $name.valueOf();
    if (attrname instanceof Array) {
        // internal use : setattr(obj, [attr, ...], value)
        // return value in stead of None
        retval = $value;
        if (attrname.length == 1) {
            attrname = attrname[0];
        } else {
            $obj = @{{getattr}}($obj, attrname.slice(0, attrname.length - 1));
            attrname = attrname[attrname.length - 1];
        }
    } else {
        retval = ${0,None}$;
    }
    if (typeof attrname != 'string') {
        return @{{raise}}($new(@{{TypeError}}, B$str("attribute name must be string, not '" + _typeof($name) + "'")));
    }
    if ($obj['$inst'] === false ||  $obj['__class__'] === @{{module}}) {
        obj = $obj['$dict'];
        if ($value['__class__'] === @{{instancemethod}}) {
            $value = $value['im_func'];
        }
    } else if ($obj['$inst'] === true) {
        mro = $obj['__class__']['__mro__'];
        // Check for __set__ (@property)
        attr = typeof $obj['$dict'] != "undefined" ? $obj['$dict'][$name] : [][1];
        if (typeof attr == "undefined") {
            for (var i = 0; i < mro.length; i++) {
                attr = mro[i][$name];
                if (typeof attr == "undefined") {
                    attr = mro[i]['$dict'][$name];
                    if (typeof attr == "undefined") {
                        continue;
                    }
                }
                break;
            }
        }
        if (typeof attr != "undefined" && attr['$inst'] === true) {
            attr = @{{_getattr}}(attr, '__set__');
            if (typeof attr != "undefined") {
                return @{{fcall}}(this, null, attr, null, $obj, $value);
            }
        }
        slots = mro[0]['__slots__'];
        if (typeof slots != "undefined") {
            if (!(slots instanceof Array)) {
                slots = slots.__array instanceof Array ? slots.__array : [];
            }
            if (slots.indexOf(attrname) < 0) {
                return @{{raise}}($new(@{{AttributeError}}, B$str("'" + $obj.__class__.__name__ + "' object has no attribute '" + attrname + "'")));
            }
            if (typeof $obj['__class__']['$dict'][attrname] != 'undefined') {
                return @{{raise}}($new(@{{AttributeError}}, B$str("'" + $obj.__class__.__name__ + "' object attribute '" + attrname + "' is read-only")));
            }
            obj = $obj['$dict'];
        } else {
            // Check for __setattr__
            for (var i = 0; i < mro.length - 1; i++) { // length - 1: last is always object, which we won't call but handle here
                __setattr__ = mro[i]['__setattr__'];
                if (typeof __setattr__ == "undefined") {
                    __setattr__ = mro[i]['$dict']['__setattr__'];
                    if (typeof __setattr__ == "undefined") {
                        continue;
                    }
                }
                return mcall(this, null, $obj, '__setattr__', $name, $value);
            }
            dict$setitem($obj['__dict__'], attrname, $value);
            return retval;
        }
    }
    obj[attrname] = $value;
    return retval;
});

func(${0,module}$, null, ${0,g}$, 'delattr', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $name) {
    var attr, __delattr__,  __delete__, mro, obj,
        attrname = typeof $name['__s'] != "undefined" ? attrname = $name['__s'] : attrname = $name.valueOf();
    if (typeof attrname != 'string') {
        return @{{raise}}($new(@{{TypeError}}, B$str("attribute name must be string, not '" + _typeof($name) + "'")));
    }
    if ($obj['$inst'] === false ||  $obj['__class__'] === @{{module}}) {
        obj = $obj['$dict'];
    } else if ($obj['$inst'] === true) {
        mro = $obj['__class__']['__mro__'];
        // Check for __delete__ (@property)
        attr = typeof $obj['$dict'] != "undefined" ? $obj['$dict'][$name] : [][1];
        if (typeof attr == "undefined") {
            for (var i = 0; i < mro.length; i++) {
                attr = mro[i][$name];
                if (typeof attr == "undefined") {
                    attr = mro[i]['$dict'][$name];
                    if (typeof attr == "undefined") {
                        continue;
                    }
                }
                break;
            }
        }
        if (typeof attr != "undefined" && attr['$inst'] === true) {
            attr = @{{_getattr}}(attr, '__delete__');
            if (typeof attr != "undefined") {
                return @{{fcall}}(this, null, attr, null, $obj);
            }
        }
        slots = mro[0]['__slots__'];
        if (typeof slots != "undefined") {
            if (!(slots instanceof Array)) {
                slots = slots.__array instanceof Array ? slots.__array : [];
            }
            if (slots.indexOf(attrname) < 0) {
                return @{{raise}}($new(@{{AttributeError}}, B$str("'" + $obj.__class__.__name__ + "' object has no attribute '" + attrname + "'")));
            }
            if (typeof $obj['__class__']['$dict'][attrname] != 'undefined') {
                return @{{raise}}($new(@{{AttributeError}}, B$str("'" + $obj.__class__.__name__ + "' object attribute '" + attrname + "' is read-only")));
            }
            obj = $obj['$dict'];
        } else {
            // Check for __delattr__
            for (var i = 0; i < mro.length - 1; i++) { // length - 1: last is always object, which we won't call but handle here
                __delattr__ = mro[i]['__delattr__'];
                if (typeof __delattr__ == "undefined") {
                    __delattr__ = mro[i]['$dict']['__delattr__'];
                    if (typeof __delattr__ == "undefined") {
                        continue;
                    }
                }
                return mcall(this, null, $obj, '__delattr__', $name, $value);
            }
            if (typeof $obj['$dict'][attrname] == "undefined") {
                return @{{raise}}($new(@{{AttributeError}}, B$str($obj['__class__']['__name__'] + " instance has no attribute '" + $name + "'")));
            }
            dict$delitem($obj['__dict__'], attrname);
            return ${0,None}$;
        }
    }
    if (typeof obj == "undefined" || typeof obj[attrname] == "undefined") {
        if ($obj['$inst'] === true) {
            return @{{raise}}($new(@{{AttributeError}}, B$str("'" + $obj['__class__']['__name__'] + "' object has no attribute '" + $name + "'")));
        }
        return @{{raise}}($new(@{{AttributeError}}, B$str(attrname)));
    }
    delete obj[attrname];
    return ${0,None}$;
});

func(${0,module}$, null, ${0,g}$, '_getattr', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $name) {
    var v, mro$, names$;
${1, getattribute, mro$, v, $obj, $name}$
${1, bind_method, v, v, $obj, _meth_src}$
    return v;
});

func(${0,module}$, null, ${0,g}$, 'getattr', ${0, function}$, ['obj', 'name', 'value'], null, null, [null], function($obj, $name, $value) {
    var attr, obj = $obj;
${1, getattributes, attr, obj, $name, $value}$
    return attr;
});

func(${0,module}$, null, ${0,g}$, 'hasattr', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $name) {
    var v, mro$, names$;
    if (typeof $name != 'string') {
        if (typeof $name.__s == "undefined") {
            return @{{raise}}($new(@{{TypeError}}, B$str("attribute name must be string, not '" + _typeof($name) + "'")));
        }
        $name = $name.valueOf()
    }
${1, getattribute, mro$, v, $obj, $name}$
    if (typeof v == 'undefined') {
        return @{{False}};
    }
    return @{{True}};
});

func(${0,module}$, null, ${0,g}$, 'setitem', ${0, function}$, ['obj', 'name', 'value'], null, null, null, function($obj, $name, $value) {
    var meth;
${1, getattribute, mro$, meth, $obj, '__setitem__'}$
    if (typeof meth != "undefined") {
${2, bind_method, meth, meth, $obj, _meth_src}$
        return @{{fcall}}(this, null, meth, $obj, $name, $value);
    }
    if (typeof $obj['$inst'] != "undefined") {
        return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) +"' object does not support item assignment")));
    }
    if (typeof $obj instanceof Array) {
        var idx = $name.valueOf();
        if (typeof idx == 'number') {
            $obj[idx] = $value;
            return $value;
        }
        return @{{raise}}($new(@{{TypeError}}, B$str("Javascript array needs number indexes not '" + _typeof($name) + "'")));
    }
    if (typeof $obj == 'object') {
        var name = $name.valueOf();
        if (typeof name == 'string') {
            $obj[name] = $value;
        } else {
            $obj[$name] = $value;
        }
        return $value;
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' does not support item assignment")));
});

func(${0,module}$, null, ${0,g}$, 'setslice', ${0, function}$, ['obj', 'slice', 'value'], null, null, null, function($obj, $slice, $value) {
    var meth, attrs = $slice.length == 2 ? ['__setslice__', '__setitem__'] : ['__setitem__'];
    for (var attri = 0 ; attri < attrs.length; attri++) {
        var attr = attrs[attri];
${2, getattribute, mro$, meth, $obj, attr}$
        if (typeof meth != "undefined") {
${3, bind_method, meth, meth, $obj, _meth_src}$
            if (attr == '__setslice__') {
                var start = $slice[0] === ${0,None}$ ? @{{int_zero}} : $slice[0],
                    stop = $slice[1] === ${0,None}$ ? @{{int_max_index}} : $slice[1];
                return @{{fcall}}(this, null, meth, $obj, start, stop, $value);
            }
            $slice = $new.apply(null, [@{{slice}}].concat($slice));
            return @{{fcall}}(null, null, meth, $obj, $slice, $value);
        }
    }
    if (typeof $obj['$inst'] != "undefined") {
        return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' object does not support item assignment")));
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' does not support slice assignment")));
});

func(${0,module}$, null, ${0,g}$, 'delitem', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $name) {
    var meth;
${1, getattribute, mro$, meth, $obj, '__delitem__'}$
    if (typeof meth != "undefined") {
${2, bind_method, meth, meth, $obj, _meth_src}$
        return @{{fcall}}(this, null, meth, $obj, $name);
    }
    if (typeof $obj['$inst'] != "undefined") {
        return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' object does not support item deletion")));
    }
    if (typeof $obj instanceof Array) {
        var idx = $name.valueOf();
        if (typeof idx == 'number') {
            // TODO: determine if a Array.splice is needed
            delete $obj[idx];
            return ${0,None}$
        }
        return @{{raise}}($new(@{{TypeError}}, B$str("Javascript array needs number indexes not '" + _typeof($name) + "'")));
    }
    if (typeof $obj == 'object') {
        var name = $name.valueOf();
        if (typeof name == 'string') {
            delete $obj[name];
        } else {
            delete $obj[$name];
        }
        return ${0,None}$
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' does not support item deletion")));
});

func(${0,module}$, null, ${0,g}$, 'delslice', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $slice) {
    var meth, attrs = $slice.length == 2 ? ['__delslice__', '__delitem__'] : ['__delitem__'];
    for (var attri = 0 ; attri < attrs.length; attri++) {
        var attr = attrs[attri];
${2, getattribute, mro$, meth, $obj, attr}$
        if (typeof meth != "undefined") {
${3, bind_method, meth, meth, $obj, _meth_src}$
            if (attr == '__delslice__') {
                var start = $slice[0] === ${0,None}$ ? @{{int_zero}} : $slice[0],
                    stop = $slice[1] === ${0,None}$ ? @{{int_max_index}} : $slice[1];
                return @{{fcall}}(this, null, meth, $obj, start, stop);
            }
            $slice = $new.apply(null, [@{{slice}}].concat($slice));
            return @{{fcall}}(null, null, meth, $obj, $slice);
        }
    }
    if (typeof $obj['$inst'] != "undefined") {
        return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' object does not support item deletion")));
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' does not support slice deletion")));
});

func(${0,module}$, null, ${0,g}$, 'getitem', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $name) {
    var meth;
${1, getattribute, mro$, meth, $obj, '__getitem__'}$
    if (typeof meth != "undefined") {
${2, bind_method, meth, meth, $obj, _meth_src}$
        return @{{fcall}}(this, null, meth, $obj, $name);
    }
    if (typeof $obj['$inst'] != "undefined") {
        return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' object is unsubscriptable")));
    }
    if (typeof $obj instanceof Array) {
        var idx = $name.valueOf();
        if (typeof idx == 'number') {
            return $obj[idx];
        }
        return @{{raise}}($new(@{{TypeError}}, B$str("Javascript array needs number indexes not '" + _typeof($name) + "'")));
    }
    if (typeof $obj == 'object') {
        var name = $name.valueOf();
        if (typeof name == 'string') {
            return $obj[name];
        } else {
            return $obj[$name];
        }
        return ${0,None}$
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) +"' is unsubscriptable")));
});

func(${0,module}$, null, ${0,g}$, 'getslice', ${0, function}$, ['obj', 'name'], null, null, null, function($obj, $slice) {
    var meth, attrs = $slice.length == 2 ? ['__getslice__', '__getitem__'] : ['__getitem__'];
    for (var attri = 0 ; attri < attrs.length; attri++) {
        var attr = attrs[attri];
${2, getattribute, mro$, meth, $obj, attr}$
        if (typeof meth != "undefined") {
${3, bind_method, meth, meth, $obj, _meth_src}$
            if (attr == '__getslice__') {
                var start = $slice[0] === ${0,None}$ ? @{{int_zero}} : $slice[0],
                    stop = $slice[1] === ${0,None}$ ? @{{int_max_index}} : $slice[1];
                return @{{fcall}}(this, null, meth, $obj, start, stop);
            }
            $slice = $new.apply(null, [@{{slice}}].concat($slice));
            return @{{fcall}}(null, null, meth, $obj, $slice);
        }
    }
    if (typeof $obj['$inst'] != "undefined") {
        return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) + "' object is unsubscriptable")));
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("'" + _typeof($obj) +"' does not support slices")));
});

@{{type}} = type(${0,module}$, 'type', [@{{object}}], {}, []);
func(${0,module}$, null, @{{type}}, '__new__', ${0, jsmethod}$, ['cls', 'clsname', 'bases', 'dict'], null, null, [null, null], function($cls, $clsname, $bases, $dict) {
    var narg = arguments.length;
    while (arguments[--narg] === null);
    if (narg == 3) {
${2, type_class, cls, this, $clsname, $bases, $dict}$
        cls.toString = function () { return "<class " + $clsname + ">";};
        return cls;
    }
    if (narg == 1) {
        return $clsname['__class__'];
    }
    @{{raise}}($new(@{{TypeError}}, B$str("type() takes 1 or 3 arguments")));
});



@{{object}}['__bases__'] = B$tuple(@{{object}}['__bases__']);
@{{tuple}}['__bases__'] = B$tuple(@{{tuple}}['__bases__']);
@{{dictproxy}}['__bases__'] = B$tuple(@{{dictproxy}}['__bases__']);
@{{type}}['__bases__'] = B$tuple(@{{type}}['__bases__']);
@{{module}}['__bases__'] = B$tuple(@{{module}}['__bases__']);
@{{basestring}}['__bases__'] = B$tuple(@{{basestring}}['__bases__']);
@{{str}}['__bases__'] = B$tuple(@{{str}}['__bases__']);
@{{classmethod}}['__bases__'] = B$tuple(@{{classmethod}}['__bases__']);

@{{object}}['__dict__'] = B$dictproxy(@{{object}}['$dict']);
@{{tuple}}['__dict__'] = B$dictproxy(@{{tuple}}['$dict']);
@{{dictproxy}}['__dict__'] = B$dictproxy(@{{dictproxy}}['$dict']);
@{{type}}['__dict__'] = B$dictproxy(@{{type}}['$dict']);
@{{module}}['__dict__'] = B$dictproxy(@{{module}}['$dict']);
@{{basestring}}['__dict__'] = B$dictproxy(@{{basestring}}['$dict']);
@{{str}}['__dict__'] = B$dictproxy(@{{str}}['$dict']);
@{{classmethod}}['__dict__'] = B$dictproxy(@{{classmethod}}['$dict']);

@{{object}}['__name__'] = B$str("object");
@{{tuple}}['__name__'] = B$str("tuple");
@{{dictproxy}}['__name__'] = B$str("dictproxy");
@{{type}}['__name__'] = B$str("type");
@{{module}}['__name__'] = B$str("module");
@{{basestring}}['__name__'] = B$str("basestring");
@{{str}}['__name__'] = B$str("str");
@{{classmethod}}['__name__'] = B$str("classmethod");

@{{object}}['__class__'] = @{{type}};
@{{tuple}}['__class__'] = @{{type}};
@{{dictproxy}}['__class__'] = @{{type}};
@{{type}}['__class__'] = @{{type}};
@{{module}}['__class__'] = @{{type}};
@{{basestring}}['__class__'] = @{{type}};
@{{str}}['__class__'] = @{{type}};
@{{classmethod}}['__class__'] = @{{type}};

func(${0,module}$, null, @{{object}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{tuple}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{dictproxy}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{type}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{module}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{basestring}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{str}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);
func(${0,module}$, null, @{{classmethod}}, '__call__', ${0, classmethod}$, null, 'args', 'kwargs', null, $newext, true);


// Add or complete some essential types
var B$__init__ = func(${0,module}$, null, @{{object}}, '__init__', ${0, function}$, ['self'], 'args', 'kwargs', null, function($self) {
    return null;
});

@{{staticmethod}} = type(${0,module}$, 'staticmethod', [@{{object}}], {}, []);
func(${0,module}$, null, @{{staticmethod}}, '__init__', ${0, function}$, ['self', 'function'], null, null, null, function($self, $function) {
    $self['im_func'] = $function;
    return ${0,None}$;
});
func(${0,module}$, null, @{{staticmethod}}, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self['$inst'] === true) {
        return B$str("<staticmethod object of " + $self['im_func_']['__name__'] + ">");
    }
    return B$str("<type staticmethod >");
});
func(${0,module}$, null, @{{staticmethod}}, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self['$inst'] === true) {
        return B$str("<staticmethod object of " + $self['im_func_']['__name__'] + ">");
    }
    return B$str("<type staticmethod >");
});

func(${0,module}$, null, @{{classmethod}}, '__init__', ${0, function}$, ['self', 'function'], null, null, null, function($self, $function) {
    $self['im_func'] = $function;
    return ${0,None}$;
});
func(${0,module}$, null, @{{classmethod}}, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self['$inst'] === true) {
        return B$str("<classmethod object of " + $self['im_func_']['__name__'] + ">");
    }
    return B$str("<type classmethod >");
});
func(${0,module}$, null, @{{classmethod}}, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self['$inst'] === true) {
        return B$str("<classmethod object of " + $self['im_func_']['__name__'] + ">");
    }
    return B$str("<type classmethod >");
});

@{{instancemethod}} = type(${0,module}$, 'instancemethod', [@{{object}}], {}, []);
func(${0,module}$, null, @{{instancemethod}}, '__new__', ${0, jsmethod}$, ['cls', 'function', 'instance', 'class'], null, null, null, function($cls, $function, $instance, $class) {
${1, __new__, instance, $cls}$
    instance['im_func'] = $function;
    instance['im_self'] = $instance;
    instance['im_class'] = $class;
    return instance;
});
func(${0,module}$, null, @{{instancemethod}}, '__init__', ${0, function}$, ['self', 'function', 'instance', 'class'], null, null, null, function($self, $function, $instance, $class) {
    return null;
});
func(${0,module}$, null, @{{instancemethod}}, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self['$inst'] === true) {
        if ($self['im_self'] === null) {
            return B$str("<unbound method " + $self['im_class']['__name__'] + "." + $self['im_func']['__name__'] + ">");
        }
        if ($self['im_self']['$inst'] === true) {
            return B$str("<bound method " + $self['im_class']['__name__'] + "." + $self['im_func']['__name__'] + ">");
        }
        return B$str("<bound method type." + $self['im_func']['__name__'] + "of <" + $self['im_class']['__name__'] + ">>");
    }
    return B$str("<instance method>");
});
func(${0,module}$, null, @{{instancemethod}}, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self['$inst'] === true) {
        if ($self['im_self'] === null) {
            return B$str("<unbound method " + $self['im_class']['__name__'] + "." + $self['im_func']['__name__'] + ">");
        }
        if ($self['im_self']['$inst'] === true) {
            return B$str("<bound method " + $self['im_class']['__name__'] + "." + $self['im_func']['__name__'] + ">");
        }
        return B$str("<bound method type." + $self['im_func']['__name__'] + "of <" + $self['im_class']['__name__'] + ">>");
    }
    return B$str("<instance method>");
});

// Complete the rest of the methods of the object type
func(${0,module}$, null, @{{object}}, '__setattr__', ${0, function}$, ['self', 'name', 'value'], null, null, null, function($self, $name, $value) {
    if (typeof $name != 'string') {
        @{{raise}}($new(@{{TypeError}}, B$str("attribute name must be string")));
    }
    if ($self['$inst'] !== true) {
        @{{raise}}($new(@{{TypeError}}, B$str("can't apply this __setattr__ to type object")));
    }
    if ($self['__class__']['__slots__'] != "undefined") {
        if (typeof $self['__class__'][$name] != "undefined") {
            @{{raise}}($new(@{{AttributeError}}, B$str("'" + $self['__class__']['__name__'] + "' object has no attribute '" + $name + "'")));
        }
    }
    // TODO: check for __set__ on $self[$name]
    $self[$name] = $value;
    // TODO: func -> instancemethod
});
func(${0,module}$, null, @{{object}}, '__getattribute__', ${0, function}$, ['self', 'name'], null, null, null, function($self, $name) {
    var v, mro$, name = $name.valueOf();
${1, attr_args_validate, $self, $name}$
    mro$ = $self['__class__']['__mro__'];
    for (var mro_i$ = 0; mro_i$ < mro$.length; mro_i$++) {
        if (typeof mro$[mro_i$][name] != "undefined") {
            v = mro$[mro_i$][name]
            break;
        }
        if (typeof mro$[mro_i$]['$dict'][name] != "undefined") {
            v = mro$[mro_i$]['$dict'][name];
            break;
        }
    }
    if (typeof v == "undefined") {
        return @{{raise}}($new(@{{AttributeError}}, B$str("'" + $self['__class__']['__name__'] + "' object has no attribute '" + name + "'")));
    }
${1, bind_method, v, v, $self, $self}$
    return v;
});
func(${0,module}$, null, @{{object}}, '__delattr__', ${0, function}$, ['self', 'name'], null, null, null, function($self, $name) {
    if (typeof $name != 'string') {
        if ($name['__class__'] === @{{str}}) {
            $name = $name.__s;
        } else {
            @{{raise}}($new(@{{TypeError}}, B$str("attribute name must be string")));
        }
    }
    if ($self['$inst'] !== true) {
        @{{raise}}($new(@{{TypeError}}, B$str("can't apply this __delattr__ to type object")));
    }
    // TODO: check for __delete__ on $self[$name]
    if (typeof $self[$name] == "undefined") {
        @{{raise}}($new(@{{AttributeError}}, $name));
    }
    delete $self[$name];
});
func(${0,module}$, null, @{{object}}, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
    var s;
    if ($self['$inst'] === true) {
        s = "instance of ";
        if (typeof self['__class__']['$dict'] != "undefined") {
            return B$str(s + self['__class__']['$dict']['__name__']);
        }
    } else if ($self['$inst'] === false) {
        s = "class ";
    } else {
        s = "javascript " + typeof self + " ";
    }
    if ($self['__module__']) {
        s += self['__module__'] + ".";
    }
    if (typeof self['__name__'] != "undefined") {
        return B$str(s + self['__name__']);
    }
    return B$str(s + "<unknown>");
});
func(${0,module}$, null, @{{object}}, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
    var s;
    if ($self['$inst'] === true) {
        s = "<instance of ";
    } else if ($self['$inst'] === false) {
        s = "<class ";
    } else {
        s = "<javascript " + typeof self + " ";
    }
    if ($self['__module__']) {
        s += self['__module__'] + ".";
    }
    if (typeof self['__name__'] != "undefined") {
        return B$str(s + self['__name__'] + ">");
    }
    return B$str(s + "<unknown>>");
});
var $next_hash_id$ = $min_int
    $hash_id_prefix$ = '#id#',
    $hash_id_name$ = '$H';
func(${0,module}$, null, @{{object}}, '__hash__', ${0, function}$, ['self'], null, null, null, function($self) {
    if (typeof $self[$hash_id_name$] != "undefined" && $self[$hash_id_name$] !== null) {
        return $self[$hash_id_name$];
    }
    if ($next_hash_id$ == $max_int) {
        // I don't think we'll ever get here, but you never know...
        $next_hash_id$ = $min_int;
        $hash_id_prefix$ += '#';
    }
    $self[$hash_id_name$] = $hash_id_prefix$ + $next_hash_id$++;
    return $self[$hash_id_name$];
});

func(${0,module}$, null, ${0,g}$, 'hash', ${0, function}$, ['obj'], null, null, null, function($obj) {
    var h;
${1, hash, $obj, h}$
    return h;
});
init_short_names$(true);

@{{None}} = null;
@{{_NoneStr}} = B$str("None")
""")
# Need to define NoneType and None early, since it's used
# in several function definitions
class NoneType(object):
    def __str__(self):
        return "None"
    def __repr__(self):
        return "None"
JS("""
@{{__doc__}} = @{{None}} = $new(@{{NoneType}});
@{{None}}['valueOf'] = function() {return null;};
@{{None}}['toString'] = function() {return 'None';};
init_short_names$(true);



// Complete the module type
func(${0,module}$, null, @{{module}}, '__init__', ${0, function}$, ['self', 'name'], null, null, [[]], function($self, $name) {
    if (typeof $name == 'string') {
        $self['__name__'] = typeofB$str($name);
    } else {
        $self['__name__'] = $name;
    }
    $self['__doc__'] = ${0,None}$;
    $self['$module_init'] = function($name) {};
    $self['__class__'] = @{{module}};
    return ${0,None}$;
});

// Complete the dictproxy type
func(${0,module}$, null, @{{dictproxy}}, '__new__', ${0, jsmethod}$, ['cls', 'obj'], null, null, null, function($cls, $obj) {
${1, __new__, instance, @{{dictproxy}}}$
    instance['__object'] = $obj;
    instance['valueOf'] = function() {return this.__object;};
    instance['toString'] = function() {return this.__object.toString();};
    return instance;
});
func(${0,module}$, null, @{{dictproxy}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, [[]], function($self, $obj) {
    return ${0,None}$;
});
// TODO: methods for dictproxy (see dir(Foo.__dict__))

// Create the dict type
// class dict
@{{dict}} = type(${0,module}$, 'dict', [@{{object}}], {}, []);
func(${0,module}$, null, @{{dict}}, '__new__', ${0, jsmethod}$, ['cls', 'iterable'], null, 'kwargs', [[]], function ($cls, $iterable, $kwargs) {
${1, __new__, instance, $cls}$
    instance['__object'] = {};
    instance['__object_hash'] = {};
    instance['__object_head'] = null;
    instance['__object_tail'] = null;
    instance['_length'] = 0;
    instance['valueOf'] = function() {return this.__object;};
    instance['getObject'] = function() {return this.__object;}; // backwards compatability TODO: remove this method
    instance['toString'] = function() {return this.__object.toString();};
    return instance;
});
func(${0,module}$, null, @{{dict}}, '__init__', ${0, function}$, ['self', 'iterable'], null, 'kwargs', [[]], function ($self, $iterable, $kwargs) {
    var k, v, h, obj, objH, node, head, tail;
    head = tail = {'prev': null, 'next': null, 'key': null}
    obj = $self.__object;
    objH = $self.__object_hash;
    if ($iterable instanceof Array) {
        for (var i = 0; i < $iterable.length; i++) {
            if ($iterable[i] instanceof Array && $iterable[i].length == 2) {
                k = $iterable[i][0];
                v = $iterable[i][1];
                if (typeof k != "undefined" && typeof v != "undefined") {
                    h = @{{hash}}(k);
                    tail.next = node = {'prev': tail, 'next': null, 'key': k}
                    tail = node;
                    objH[h] = node;
                    obj[h] = v;
                    $self['_length']++;
                } else {
                    if (typeof k == "undefined") {
                        @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (key is undefined)")));
                    }
                    @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (value for '" + k + "' is undefined)")));
                }
            }
        }
    } else if ($iterable.valueOf() === null) {
        @{{raise}}($new(@{{TypeError}}, B$str("'NoneType' is not iterable")));
    } else if (typeof $iterable.__object_hash == 'object') {
        for (h in $iterable.__object_hash) {
            k = $iterable.__object_hash[h].key;
            v =  $iterable.__object[h];
            if (typeof k != "undefined" && typeof v != "undefined") {
                tail.next = node = {'prev': tail, 'next': null, 'key': k}
                tail = node;
                objH[h] = node;
                obj[h] = v;
                $self['_length']++;
            } else {
                if (typeof k == "undefined") {
                    @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (key is undefined)")));
                }
                @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (value for '" + k + "' is undefined)")));
            }
        }
    } else if (typeof $iterable['$inst'] == "undefined") {
        for (k in $iterable) {
            v = $iterable[k];
            if (typeof k != "undefined" && typeof v != "undefined") {
                h = @{{hash}}(k);
                tail.next = node = {'prev': tail, 'next': null, 'key': k}
                tail = node;
                objH[h] = node;
                obj[h] = v;
                $self['_length']++;
            } else {
                if (typeof k == "undefined") {
                    @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (key is undefined)")));
                }
                @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (value for '" + k + "' is undefined)")));
            }
        }
    } else {
        var iter, next, i, k, v, _stopiter = @{{stopiter}};
        iter = @{{fcall}}(this, null, @{{iter}}, null, $iterable);
        next = @{{getattr}}(iter, 'next');
        for (;;) {
            @{{stopiter}} = true;
            i = @{{fcall}}(this, null, next, iter);
            @{{stopiter}} = _stopiter;
            if (i === @{{StopIter}}) {
                break;
            }
            k = @{{getitem}}(i, @{{int_zero}});
            v = @{{getitem}}(i, @{{int_one}});
            @{{dict}}['$dict']['__setitem__']($self, k, v);
        }
    }
    if (typeof $kwargs != "undefined" && $kwargs !== null && $kwargs['_length'] > 0) {
        $iterable = $kwargs;
        for (h in $iterable.__object_hash) {
            k = $iterable.__object_hash[h].key;
            v =  $iterable.__object[h];
            if (typeof k != "undefined" && typeof v != "undefined") {
                tail.next = node = {'prev': tail, 'next': null, 'key': k}
                tail = node;
                objH[h] = node;
                obj[h] = v;
                $self['_length']++;
            } else {
                if (typeof k == "undefined") {
                    @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (key is undefined)")));
                }
                @{{raise}}($new(@{{TypeError}}, B$str("Invalid iterable (value for '" + k + "' is undefined)")));
            }
        }
    }
    if (head !== tail) {
        $self.__object_head = head.next;
        $self.__object_head.prev = null;
        $self.__object_tail = tail;
    }
    return ${0,None}$;
});
func(${0,module}$, null, @{{dict}}, 'clear', ${0, function}$, ['self'], null, null, null, function ($self) {
    $self.__object = {};
    $self.__object_hash = {};
    $self.__object_head = null;
    $self.__object_tail = null;
    $self['_length'] = 0;
    return ${0,None}$;
});
func(${0,module}$, null, @{{dict}}, 'copy', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{dict}}, $self);
});
func(${0,module}$, null, @{{dict}}, '__hash__', ${0, function}$, ['self'], null, null, null, function ($self) {
    @{{raise}}($new(@{{TypeError}}, B$str("dict objects are unhashable")));
});
func(${0,module}$, null, @{{dict}}, '__setitem__', ${0, function}$, ['self', 'key', 'value'], null, null, null, function ($self, $key, $value) {
    if (typeof $value == "undefined") {
        @{{raise}}($new(@{{ValueError}}, B$str("Value for key '" + $key + "' is undefined")));
    }
    var node, h = @{{hash}}($key);
    if (typeof $self.__object_hash[h] == "undefined") {
        if ($self.__object_head === null) {
            $self.__object_head = $self.__object_tail = {'prev': null, 'next': null, 'key': $key};
        } else {
            node = {'prev': $self.__object_tail, 'next': null, 'key': $key}
            $self.__object_tail.next = node;
            $self.__object_tail = node;
        }
        $self['_length']++;
        $self.__object_hash[h] = $self.__object_tail;
    }
    $self.__object[h] = $value;
    return ${0,None}$;
});
func(${0,module}$, null, @{{dict}}, '__getitem__', ${0, function}$, ['self', 'key'], null, null, null, function ($self, $key) {
    var h = @{{hash}}($key);
    if (typeof $self.__object_hash[h] == "undefined") {
        @{{raise}}($new(@{{KeyError}}, $key));
    }
    return $self.__object[h];
});
func(${0,module}$, null, @{{dict}}, '__delitem__', ${0, function}$, ['self', 'key'], null, null, null, function ($self, $key) {
    var node, h = @{{hash}}($key);
    node = $self.__object_hash[h];
    if (typeof node == "undefined") {
        @{{raise}}($new(@{{KeyError}}, $key));
    }
    if (node.prev === null) {
        $self.__object_head = node.next;
        if (node.next !== null) {
            $self.__object_head.prev = null;
        }
    } else if (node.next === null) {
        $self.__object_tail = node.prev;
        $self.__object_tail.next = null;
    } else {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    $self['_length']--;
    delete $self.__object_hash[h];
    delete $self.__object[h];
    delete node;
    return ${0,None}$;
});
func(${0,module}$, null, @{{dict}}, 'setdefault', ${0, function}$, ['self', 'key', 'value'], null, null, [${0,None}$], function ($self, $key, $value) {
    var h = @{{hash}}($key);
    if (typeof $self.__object_hash[h] == "undefined") {
        @{{dict}}['$dict']['__setitem__']($self, $key, $value);
        return $value;
    }
    return $self.__object[h];
});
func(${0,module}$, null, @{{dict}}, 'get', ${0, function}$, ['self', 'key', 'value'], null, null, [${0,None}$], function ($self, $key, $value) {
    var h = @{{hash}}($key);
    if (typeof $self.__object_hash[h] == "undefined") {
        return $value;
    }
    return $self.__object[h];
});
func(${0,module}$, null, @{{dict}}, 'pop', ${0, function}$, ['self', 'key', 'value'], null, null, [null], function ($self, $key, $value) {
    var h = @{{hash}}($key);
    if (typeof $self.__object_hash[h] == "undefined") {
        if ($value === null) {
            @{{raise}}($new(@{{KeyError}}, $key));
        }
        return $value;
    }
    $value = $self.__object[h];
    @{{dict}}['$dict']['__delitem__']($self, $key);
    return $value;
});
func(${0,module}$, null, @{{dict}}, 'popitem', ${0, function}$, ['self'], null, null, [null], function ($self) {
    var k, v, h, node;
    if ($self['_length'] == 0) {
        @{{raise}}($new(@{{KeyError}}, B$str("popitem(): dictionary is empty")));
    }
    node = $self.__object_tail;
    k = node['key'];
    h = @{{hash}}(k);
    v = $self.__object[h];
    $self.__object_tail = node.prev;
    if ($self.__object_tail === null) {
        $self.__object_head = null;
    } else {
        $self.__object_tail.next = null;
    }
    $self['_length']--;
    delete $self.__object[h];
    delete $self.__object_hash[h];
    delete node;
    return $new(@{{tuple}}, [k, v]);
});
func(${0,module}$, null, @{{dict}}, '__nonzero__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['_length'] == 0) {
        return @{{False}};
    }
    return @{{True}};
});
func(${0,module}$, null, @{{dict}}, '__len__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{int}}, $self['_length']);
});
func(${0,module}$, null, @{{dict}}, '__contains__', ${0, function}$, ['self', 'key'], null, null, null, function ($self, $key) {
    return typeof $self.__object_hash[@{{hash}}($key)] == "undefined" ? @{{False}} : @{{True}};
});
func(${0,module}$, null, @{{dict}}, 'has_key', ${0, function}$, ['self', 'key'], null, null, null, function ($self, $key) {
    return typeof $self.__object_hash[@{{hash}}($key)] == "undefined" ? @{{False}} : @{{True}};
});
func(${0,module}$, null, @{{dict}}, 'keys', ${0, function}$, ['self'], null, null, null, function ($self) {
    var i, keys = $new(@{{list}}, []),
        __array = keys.__array,
        objH = $self.__object_hash;
    i = 0;
    for (var h in objH) {
        __array[i] = objH[h]['key'];
        i ++;
    }
    return keys;
});
func(${0,module}$, null, @{{dict}}, 'fromkeys', ${0, staticmethod}$, ['iterable', 'v'], null, null, [${0,None}$], function ($iterable, $v) {
    var iter, next, k, d = $new(@{{dict}}, []), _stopiter = @{{stopiter}};
    iter = @{{fcall}}(this, null, @{{iter}}, null, $iterable);
    next = @{{getattr}}(iter, 'next');
    for (;;) {
        @{{stopiter}} = true;
        k = @{{fcall}}(this, null, next, iter);
        @{{stopiter}} = _stopiter;
        if (k === @{{StopIter}}) {
            break;
        }
        @{{dict}}['$dict']['__setitem__'](d, k, $v);
    }
    return d;
});
func(${0,module}$, null, @{{dict}}, 'values', ${0, function}$, ['self'], null, null, null, function ($self) {
    var i, values = $new(@{{list}}, []),
        __array = values.__array,
        obj = $self.__object;
    i = 0
    for (var h in obj) {
        __array[i++] = obj[h];
    }
    return values;
});
func(${0,module}$, null, @{{dict}}, 'items', ${0, function}$, ['self'], null, null, null, function ($self) {
    var i, items = $new(@{{list}}, []),
        __array = items.__array,
        obj = $self.__object,
        objH = $self.__object_hash;
    i = 0;
    for (var h in obj) {
        __array[i++] = $new(@{{tuple}}, [objH[h]['key'], obj[h]]);
    }
    return items;
});
func(${0,module}$, null, @{{dict}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{dictionary-keyiterator}}, $self);
});
func(${0,module}$, null, @{{dict}}, 'iterkeys', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{dictionary-keyiterator}}, $self);
});
func(${0,module}$, null, @{{dict}}, 'itervalues', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{dictionary-valueiterator}}, $self);
});
func(${0,module}$, null, @{{dict}}, 'iteritems', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{dictionary-itemiterator}}, $self);
});
func(${0,module}$, null, @{{dict}}, '__cmp__', ${0, function}$, ['self', 'y'], null, null, null, function ($self, $y) {
    if ($self['$inst'] === true) {
        if ($self['__class__']['__mro__'][$self['__class__']['__mro__'].length -2] === @{{dict}}) {
            var self_sKeys = new Array(),
                other_sKeys = new Array(),
                selfLen = 0,
                otherLen = 0,
                selfObj = $self.__object,
                otherObj = $y.__object,
                selfObjH = $self.__object_hash,
                otherObjH = $y.__object_hash;
            for (sKey in selfObjH) {
               self_sKeys[selfLen++] = sKey;
            }
            for (sKey in otherObjH) {
               other_sKeys[otherLen++] = sKey;
            }
            if (selfLen < otherLen) {
                return @{{int_minus_one}};
            }
            if (selfLen > otherLen) {
                return @{{int_one}};
            }
            self_sKeys.sort();
            other_sKeys.sort();
            var c, sKey, self_k, other_k;
            for (var idx = 0; idx < selfLen; idx++) {
                sKey = self_sKeys[idx];
                self_k = selfObjH[sKey]['key'];
                if (typeof otherObjH[sKey] == 'undefined') {
                    return @{{int_minus_one}};
                }
                other_k = otherObjH[sKey]['key'];
                c = @{{cmp}}(self_k, other_k);
                if (c.__v != 0) {
                    return c;
                }
                c = @{{cmp}}(selfObj[sKey], otherObj[sKey]);
                if (c.__v != 0) {
                    return c;
                }
            }
            return @{{int_zero}};
        }
    }
    @{{raise}}($new(@{{TypeError}}, B$str("dict.__cmp__(x,y) requires y to be a 'dict'")))
});

func(${0,module}$, null, @{{dict}}, '__str__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['$inst'] === false) {
        return B$str("<type '%s'>" % $self.__name__);
    } else if ($self['$inst'] === true) {
        var key, value, s = "", obj = $self['__object'], objH = $self['__object_hash'];
        for (var h in objH) {
            key = objH[h]['key'];
            value = obj[h];
            s += ", " + @{{repr}}(key) + ": " + @{{repr}}(value);
        }
        return B$str("{" + s.substring(2) + "}");
    }
    return B$str("" + $self);
});
func(${0,module}$, null, @{{dict}}, '__repr__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if (typeof $self['$inst'] == 'boolean') {
        var key, value, i,
            s = "",
            obj = $self['__object'],
            objH = $self['__object_hash'],
            hashes = [];
        i = 0;
        for (var h in objH) {
            hashes[i++] = h;
        }
        hashes.sort();
        for (i = 0 ; i < hashes.length; i++ ) {
            h = hashes[i];
            key = objH[h]['key'];
            value = obj[h];
            s += ", " + @{{repr}}(key) + ": " + @{{repr}}(value);
        }
        return B$str("{" + s.substring(2) + "}");
    }
    return B$str("" + $self);
});

// shortcut to dict.__setitem__ (used in setattr)
var dict$setitem = @{{dict}}['$dict']['__setitem__'];
var dict$delitem = @{{dict}}['$dict']['__delitem__'];


// Complete the tuple type
// class tuple
func(${0,module}$, null, @{{tuple}}, '__new__', ${0, jsmethod}$, ['cls', 'iterable'], null, null, [[]], function ($cls, $iterable) {
${1, __new__, instance, $cls}$
    instance['__array'] = [];
    instance['valueOf'] = function() {return this.__array;};
    instance['toString'] = function() {return this['__class__']['$dict']['__str__'](this).valueOf();};
    return instance;
});
func(${0,module}$, null, @{{tuple}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, [[]], function ($self, $iterable) {
    var iter, instance = $self;

    if ($iterable instanceof Array) {
        instance.__array = $iterable.slice();
        return ${0,None}$;
    }
    if ($iterable.valueOf() === null) {
        @{{raise}}($new(@{{TypeError}}, B$str("'NoneType' is not iterable")));
    }
    iter = @{{_getattr}}($iterable, '__iter__');
    if (typeof iter != "undefined") {
        switch($iterable['__class__']) {
            case @{{tuple}}:
            case @{{list}}:
            case @{{tupleiterator}}:
            case @{{listiterator}}:
                instance.__array = $iterable.__array.slice();
                return ${0,None}$;
            case @{{listreverseiterator}}:
                instance.__array = $iterable.__array.slice();
                instance.__array.reverse();
                return ${0,None}$;
        }
        iter = @{{fcall}}(this, null, iter, $iterable);
        if (iter.__array instanceof Array) {
            instance.__array = iter.__array.slice();
            return ${0,None}$;
        }
        var __array = instance.__array;
        var item, i = 0, stopiter = @{{stopiter}};
        for (;;) {
            @{{stopiter}} = true;
            item = @{{mcall}}(this, null, iter, 'next');
            @{{stopiter}} = stopiter;
            if (item === @{{StopIter}}) {
                break;
            }
            __array[i++] = item;
        }
        return ${0,None}$;
    }
    @{{raise}}($new(@{{TypeError}}, B$str("'" + @{{repr}}($iterable) + "' is not iterable")));
});
func(${0,module}$, null, @{{tuple}}, '__len__', ${0, function}$, ['self'], null, null, null, function($self) {
    return $new(@{{int}}, $self.__array.length);
});
func(${0,module}$, null, @{{tuple}}, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self.__array.length == 0) {
        return B$str("()");
    }
    var s = '(';
    for (var i = 0; i < $self.__array.length; i++) {
        s += @{{repr}}($self.__array[i]) + ', ';
    }
    s = s.substring(0, s.length - ($self.__array.length == 1 ? 1 : 2));
    return B$str(s + ")");
});
func(${0,module}$, null, @{{tuple}}, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
    if ($self.__array.length == 0) {
        return B$str("()");
    }
    var s = '(';
    for (var i = 0; i < $self.__array.length; i++) {
        s += @{{repr}}($self.__array[i]) + ', ';
    }
    return B$str(s.substring(0, s.length - ($self.__array.length == 1 ? 1 : 2)) + ')');
});
func(${0,module}$, null, @{{tuple}}, '__hash__', ${0, function}$, ['self'], null, null, null, function($self) {
    var hash = '$tuple$';
    for (var i = 0; i < $self.__array.length; i++) {
        hash += ',' + @{{hash}}($self.__array[i]);
    }
    return hash;
});
func(${0,module}$, null, @{{tuple}}, '__getitem__', ${0, function}$, ['self', 'index'], null, null, null, function($self, $index) {
    if (typeof $index == 'number' || @{{isinstance}}($index, [@{{int}}, @{{long}}]).__v) {
        var index = typeof $index != 'number' ? $index.valueOf() : ($index < 0 ? Math.ceil($index) : Math.floor($index));
        if (index < 0) index += $self.__array.length;
        if (index < 0 || index >= $self.__array.length) {
            @{{raise}}($new(@{{IndexError}}, B$str(_typeof($self) + " index out of range")));
        }
        return $self.__array[index];
    }
    if ($index['__class__'] === @{{slice}}) {
        var start = $index['$dict']['start'].valueOf(),
            stop = $index['$dict']['stop'].valueOf(),
            step = $index['$dict']['step'].valueOf(),
            __array = [];
        if (start === null) start = 0;
        if (stop === null || stop > $self.__array.length) stop = $self.__array.length;
        if (step === null) step = 1;
        for (; start < stop; start += step) {
            __array.push($self.__array[start]);
        }
        return $new($self['__class__'], __array);
    }
    @{{raise}}($new(@{{TypeError}}, B$str(_typeof($self) + " indices must be integers, not " + typeof $index)));
});
func(${0,module}$, null, @{{tuple}}, '__getslice__', ${0, function}$, ['self', 'lower', 'upper'], null, null, null, function($self, $lower, $upper) {
    if (typeof $lower == 'number' || @{{isinstance}}($lower, [@{{int}}, @{{long}}]).__v) {
        if (typeof $upper == 'number' || @{{isinstance}}($upper, [@{{int}}, @{{long}}]).__v) {
            var lower = $lower.valueOf(),
                upper = $upper.valueOf();
            if (upper < 0) {
                upper += $self.__array.length;
            }
            return B$tuple($self.__array.slice(lower, upper));
        }
    }
    @{{raise}}($new(@{{TypeError}}, B$str(_typeof($self) + " indices must be integers, not " + typeof $index)));
});
func(${0,module}$, null, @{{tuple}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{tupleiterator}}, $self);
});
func(${0,module}$, null, @{{tuple}}, '__contains__', ${0, function}$, ['self', 'value'], null, null, null, function ($self, $value) {
    return $self.__array.indexOf($value) >= 0 ? @{{True}} : @{{False}};
});
func(${0,module}$, null, @{{tuple}}, 'count', ${0, function}$, ['self', 'value'], null, null, null, function ($self, $value) {
    var n = 0, __array = $self.__array;
    for (var i = 0; i < __array.length; i++) {
        if (@{{cmp}}(__array[i], $value)) {
            n++;
        }
    }
    return $new(@{{int}}, n);
});
// lazy... implement __cmp__ in stead of __eq__ and friends
func(${0,module}$, null, @{{tuple}}, '__cmp__', ${0, function}$, ['self', 'y'], null, null, null, function ($self, $y) {
    if ($y['__class__'] !== @{{tuple}}) {
        if (@{{isinstance}}($y, @{{tuple}}) !== @{{True}}) {
            return @{{int_one}};
        }
    }
    var n1 = $self.__array.length,
        n2 = $y.__array.length,
        a1 = $self.__array,
        a2 = $y.__array,
        n, c;
    n = (n1 < n2 ? n1 : n2);
    for (var i = 0; i < n; i++) {
        c = @{{cmp}}(a1[i], a2[i]);
        if (c !== @{{int_zero}}) return c;
    }
    if (n1 < n2) return @{{int_minus_one}};
    if (n1 > n2) return @{{int_one}};
    return @{{int_zero}};
});
func(${0,module}$, null, @{{tuple}}, '__add__', ${0, function}$, ['self', 'value'], null, null, null, function ($self, $value) {
    if ($value['__class__'] !== @{{tuple}}) {
        return @{{raise}}($new(@{{TypeError}}, B$str("can only concatenate tuple (not '" + _typeof($value) + "') to tuple")));
    }
    return B$tuple($self.__array.slice(0).concat($value.__array));
});


var empty_tuple = B$tuple([]);

//@{{NoneType}} = type(${0,module}$, 'tuple', [@{{object}}], {}, []);
// Initialize the short names, i.e., those that are available
init_short_names$(true);

@{{tupleiterator}} = type(${0,module}$, 'tupleiterator', [@{{object}}], {}, []);
func(${0,module}$, null, @{{tupleiterator}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, null, function ($self, $iterable) {
    $self['index'] = 0;
    $self['__array'] = $iterable.__array;
    return ${0,None}$;
});
func(${0,module}$, null, @{{tupleiterator}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $self;
});
func(${0,module}$, null, @{{tupleiterator}}, '__length_hint__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{int}}, $self.__array.length - $self['index']);
});
func(${0,module}$, null, @{{tupleiterator}}, 'next', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['index'] >= $self.__array.length) {
        return @{{raise}}(@{{StopIteration}});
    }
    return $self.__array[$self.index++];
});

@{{listiterator}} = type(${0,module}$, 'listiterator', [@{{object}}], {}, []);
func(${0,module}$, null, @{{listiterator}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, null, function ($self, $iterable) {
    $self['index'] = 0;
    $self['__array'] = $iterable.__array;
    return ${0,None}$;
});
func(${0,module}$, null, @{{listiterator}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $self;
});
func(${0,module}$, null, @{{listiterator}}, '__length_hint__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['index'] >= $self.__array.length) {
        return @{{int_zero}};
    }
    return $new(@{{int}}, $self.__array.length - $self['index']);
});
func(${0,module}$, null, @{{listiterator}}, 'next', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['index'] >= $self.__array.length) {
        return @{{raise}}(@{{StopIteration}});
    }
    return $self.__array[$self.index++];
});


@{{listreverseiterator}} = type(${0,module}$, 'listreverseiterator', [@{{object}}], {}, []);
func(${0,module}$, null, @{{listreverseiterator}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, null, function ($self, $iterable) {
    $self['__array'] = $iterable.__array;
    $self['index'] = $iterable.__array.length;
    return ${0,None}$;
});
func(${0,module}$, null, @{{listreverseiterator}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $self;
});
func(${0,module}$, null, @{{listreverseiterator}}, '__length_hint__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['index'] > $self.__array.length ||
        $self['index']  <= 1) {
        return @{{int_zero}};
    }
    return $new(@{{int}}, $self['index']);
});
func(${0,module}$, null, @{{listreverseiterator}}, 'next', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self['index'] < 1) {
        return @{{raise}}(@{{StopIteration}});
    }
    return $self.__array[--$self.index];
});

@{{dictionary-keyiterator}} = type(${0,module}$, 'dictionary-keyiterator', [@{{object}}], {}, []);
func(${0,module}$, null, @{{dictionary-keyiterator}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, null, function ($self, $iterable) {
    $self['index'] = 0;
    $self['iterable'] = $iterable;
    $self['node'] = $iterable.__object_head;
    $self['_length'] = $iterable._length;
    return ${0,None}$;
});
func(${0,module}$, null, @{{dictionary-keyiterator}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $self;
});
func(${0,module}$, null, @{{dictionary-keyiterator}}, '__length_hint__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self._length != $self['iterable']._length ) {
        @{{raise}}($new(@{{RuntimeError}}, B$str("dictionary changed size during iteration")));
    }
    if ($self['index'] >= $self._length) {
        return @{{int_zero}};
    }
    return $new(@{{int}}, $self._length - $self['index']);
});
func(${0,module}$, null, @{{dictionary-keyiterator}}, 'next', ${0, function}$, ['self'], null, null, null, function ($self) {
    var key;
    if ($self._length == $self['iterable']._length ) {
        if ($self['node'] === null) {
            return @{{raise}}(@{{StopIteration}});
        }
        if ($self['index'] < $self._length) {
            $self['index']++;
            key = $self['node']['key'];
            $self['node'] = $self['node'].next;
            return key;
        }
    }
    @{{raise}}($new(@{{RuntimeError}}, B$str("dictionary changed size during iteration")));
});

@{{dictionary-valueiterator}} = type(${0,module}$, 'dictionary-valueiterator', [@{{object}}], {}, []);
func(${0,module}$, null, @{{dictionary-valueiterator}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, null, function ($self, $iterable) {
    $self['index'] = 0;
    $self['iterable'] = $iterable;
    $self['node'] = $iterable.__object_head;
    $self['_length'] = $iterable._length;
    return ${0,None}$;
});
func(${0,module}$, null, @{{dictionary-valueiterator}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $self;
});
func(${0,module}$, null, @{{dictionary-valueiterator}}, '__length_hint__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self._length != $self['iterable']._length ) {
        @{{raise}}($new(@{{RuntimeError}}, B$str("dictionary changed size during iteration")));
    }
    if ($self['index'] >= $self._length) {
        return @{{int_zero}};
    }
    return $new(@{{int}}, $self._length - $self['index']);
});
func(${0,module}$, null, @{{dictionary-valueiterator}}, 'next', ${0, function}$, ['self'], null, null, null, function ($self) {
    var value;
    if ($self._length == $self['iterable']._length ) {
        if ($self['node'] === null) {
            return @{{raise}}(@{{StopIteration}});
        }
        if ($self['index'] < $self._length) {
            $self['index']++;
            value = $self['iterable']['__object'][@{{hash}}($self['node']['key'])];
            $self['node'] = $self['node'].next;
            return value;
        }
    }
    @{{raise}}($new(@{{RuntimeError}}, B$str("dictionary changed size during iteration")));
});

@{{dictionary-itemiterator}} = type(${0,module}$, 'dictionary-itemiterator', [@{{object}}], {}, []);
func(${0,module}$, null, @{{dictionary-itemiterator}}, '__init__', ${0, function}$, ['self', 'iterable'], null, null, null, function ($self, $iterable) {
    $self['index'] = 0;
    $self['iterable'] = $iterable;
    $self['node'] = $iterable.__object_head;
    $self['_length'] = $iterable._length;
    return ${0,None}$;
});
func(${0,module}$, null, @{{dictionary-itemiterator}}, '__iter__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $self;
});
func(${0,module}$, null, @{{dictionary-itemiterator}}, '__length_hint__', ${0, function}$, ['self'], null, null, null, function ($self) {
    if ($self._length != $self['iterable']._length ) {
        @{{raise}}($new(@{{RuntimeError}}, B$str("dictionary changed size during iteration")));
    }
    if ($self['index'] >= $self._length) {
        return @{{int_zero}};
    }
    return $new(@{{int}}, $self._length - $self['index']);
});
func(${0,module}$, null, @{{dictionary-itemiterator}}, 'next', ${0, function}$, ['self'], null, null, null, function ($self) {
    var key, value;
    if ($self._length == $self['iterable']._length ) {
        if ($self['node'] === null) {
            return @{{raise}}(@{{StopIteration}});
        }
        if ($self['index'] < $self._length) {
            $self['index']++;
            key = $self['node']['key'];
            value = $self['iterable']['__object'][@{{hash}}(key)];
            $self['node'] = $self['node'].next;
            return B$tuple([key, value]);
        }
    }
    @{{raise}}($new(@{{RuntimeError}}, B$str("dictionary changed size during iteration")));
});


// class str
func(${0,module}$, null, @{{str}}, '__new__', ${0, jsmethod}$, ['cls', 'value'], null, null, [''], function(cls, value) {
    if (typeof value == "undefined") {
        if (typeof cls == 'string') {
            value = cls;
            cls = @{{str}};
        } else {
            @{{raise}}($new(@{{TypeError}}, B$str("str: value is undefined")));
        }
    } else if (value['__class__'] === cls) {
        return value;
    }
    var v = value.valueOf();
${1, __new__, new_str, cls}$
    new_str['__s'] = v;
    new_str['valueOf'] = function() {return this.__s;};
    new_str['toString'] = function() {return this.__s.toString();};
    if (typeof value == 'string') {
        return new_str;
    }
    if (value === null) {
        return @{{None}}.__str__(@{{None}});
    }
    if (typeof value =='boolean') {
        return value ? @{{True}}.__str__(@{{True}}) : @{{False}}.__str__(@{{False}});
    }
    var mro$;
${2, getattribute, mro$, v, value, '__str__'}$
    if (typeof v == "undefined") {
        new_str['__s'] = String(value);
        return new_str;
    }
    if (v['__class__'] === cls) {
        return v;
    }
    if (typeof v['__class__'] != "undefined") {
        v = @{{fcall}}(this, null, v, value, value);
        if (v['__class__'] === @{{str}}) {
            return v;
        }
        if (typeof v == 'string') {
            new_str['__s'] = v;
        } else {
            @{{raise}}($new(@{{TypeError}}, B$str("__str__ returned non-string")));
        }
    } else if (typeof v == 'function') {
        if (typeof v['__name__'] != "undefined") {
            new_str['__s'] = "<function " + v['__name__'] + ">";
        } else {
            new_str['__s'] = "<javascript function " + v['name'] + ">";
        }
    } else {
        new_str['__s'] = "<javascript " + typeof v + ">";
    }
    return new_str;
});

func(${0,module}$, null, @{{str}}, '__len__', ${0, function}$, ['self'], null, null, null, function ($self) {
    return $new(@{{int}}, $self.__s.length);
});

func(${0,module}$, null, @{{str}}, 'find', ${0, function}$, ['self', 'sub', 'start', 'end'], null, null, [${0,None}$, ${0,None}$], function($self, $sub, $start, $end) {
    var self = $self.valueOf();
    var sub = $sub.valueOf();
    var start = $start.valueOf();
    var end = $end.valueOf();
    var pos = self.indexOf(sub, start);
    if (typeof end != 'number' || pos + sub.length <= end) {
        switch (pos) {
            case -1:
                break;
            case 0:
                return @{{int_zero}}
            case 1:
                return @{{int_one}}
            default:
                return $new(@{{int}}, pos);
        }
    }
    return @{{int_minus_one}};
});

func(${0,module}$, null, @{{str}}, 'rfind', ${0, function}$, ['self', 'sub', 'start', 'end'], null, null, [${0,None}$, ${0,None}$], function($self, $sub, $start, $end) {
    var self = $self.valueOf();
    var sub = $sub.valueOf();
    var start = $start.valueOf();
    var end = $end.valueOf();
    var pos;
    if (typeof start == 'number') {
        /* *sigh* - python rfind goes *RIGHT*, NOT left */
        pos = self.substring(start).lastIndexOf(sub);
        if (pos == -1) {
            return @{{int_minus_one}};
        }
        pos += start;
    } else {
        pos = self.lastIndexOf(sub);
    }
    if (typeof end == 'number') {
        if (pos + sub.length > end) return @{{int_minus_one}};
    }
    return $new(@{{int}}, pos);
});

func(${0,module}$, null, @{{str}}, 'index', ${0, function}$, ['self', 'sub', 'start', 'end'], null, null, [${0,None}$, ${0,None}$], function($self, $sub, $start, $end) {
    var i = @{{str}}['$dict']['find']($self, $sub, $start, $end);
    if (i.__v < 0) {
        @{{raise}}($new(@{{ValueError}}, B$str("substring not found")));
    }
    return i;
});

func(${0,module}$, null, @{{str}}, 'rindex', ${0, function}$, ['self', 'sub', 'start', 'end'], null, null, [${0,None}$, ${0,None}$], function($self, $sub, $start, $end) {
    var i = @{{str}}['$dict']['rfind']($self, $sub, $start, $end);
    if (i.__v < 0) {
        @{{raise}}($new(@{{ValueError}}, B$str("substring not found")));
    }
    return i;
});

func(${0,module}$, null, @{{str}}, 'partition', ${0, function}$, ['self', 'sep'], null, null, null, function($self, $sep) {
    var i = @{{str}}['$dict']['find']($self, $sep, ${0,None}$, ${0,None}$);
    if (i.__v < 0) {
        return B$tuple([$self, @{{empty_str}}, @{{empty_str}}]);
    }
    var self = $self.valueOf(), sep = $sep.valueOf();
    return B$tuple([B$str(self.substring(0, i.__v)), $sep, B$str(self.substring(sep.length + i.__v))])
});

func(${0,module}$, null, @{{str}}, 'rpartition', ${0, function}$, ['self', 'sep'], null, null, null, function($self, $sep) {
    var i = @{{str}}['$dict']['rfind']($self, $sep, ${0,None}$, ${0,None}$);
    if (i.__v < 0) {
        return B$tuple([@{{empty_str}}, @{{empty_str}}, $self]);
    }
    var self = $self.valueOf(), sep = $sep.valueOf();
    return B$tuple([B$str(self.substring(0, i.__v)), $sep, B$str(self.substring(sep.length + i.__v))])
});

func(${0,module}$, null, @{{str}}, 'count', ${0, function}$, ['self', 'sub', 'start', 'end'], null, null, [${0,None}$], function(self, sub, start, end) {
    self = self.valueOf();
    start = start.valueOf();
    end = end.valueOf();
    var pos, count = 0, n = sub.length;
    if (typeof start != 'number') start = 0;
    if (typeof end == "undefined") end = self.length;
    while (start < end) {
        pos = self.find(sub, start, end);
        if (pos < 0) break;
        count ++;
        start = pos + n;
    }
    return $new(@{{int}}, count);
});

func(${0,module}$, null, @{{str}}, 'join', ${0, function}$, ['self', 'iterable'], null, null, [${0,None}$], function($self, $iterable) {
    var s = $self.valueOf();
    var data = $iterable.valueOf();
    var text="";

    if (data instanceof Array) {
        if (data.length > 0) {
            text = data[0].valueOf()
            for (var i = 1; i < data.length; i++) {
               text += s + data[i].valueOf();
            }
        }
    } else if (typeof $iterable.__iter__ == 'function') {
        if ($iterable.__array instanceof Array) {
            text = $iterable.__array.join(s);
        } else {
            var iter = $iterable.__iter__();
            if (typeof iter.__array == 'object') {
                return iter.__array.join(s);
            }
            data = [];
            var item, i = 0;
            if (typeof iter.$genfunc == 'function') {
                while (typeof (item=iter.next(true)) != "undefined") {
                    data[i++] = item;
                }
            } else {
                try {
                    while (true) {
                        data[i++] = iter.next();
                    }
                }
                catch (e) {
                    if (e.__name__ != 'StopIteration') throw e;
                }
            }
            text = data.join(s);
        }
    }
    return $new(@{{str}}, text);
});

func(${0,module}$, null, @{{str}}, 'isdigit', ${0, function}$, ['self'], null, null, [${0,None}$], function(self) {
    return self.valueOf().match(/^\d+$/g) !== null ? @{{True}} : @{{False}};
});

func(${0,module}$, null, @{{str}}, 'isalnum', ${0, function}$, ['self'], null, null, [${0,None}$], function(self) {
    return self.valueOf().match(/^[a-zA-Z\d]+$/g) !== null ? @{{True}} : @{{False}};
});

func(${0,module}$, null, @{{str}}, 'isalpha', ${0, function}$, ['self'], null, null, [${0,None}$], function(self) {
    return self.valueOf().match(/^[a-zA-Z]+$/g) !== null ? @{{True}} : @{{False}};
});

func(${0,module}$, null, @{{str}}, 'isupper', ${0, function}$, ['self'], null, null, [${0,None}$], function(self) {
    return self.valueOf().match(/[a-z]/g) === null ? @{{True}} : @{{False}};
});

func(${0,module}$, null, @{{str}}, 'replace', ${0, function}$, ['self', 'old', 'replace', 'count'], null, null, [${0,None}$], function(self, old, replace, count) {
    self = self.valueOf();
    old = old.valueOf();
    replace = replace.valueOf();
    count = count.valueOf();
    var do_max=false;
    var start=0;
    var text="";
    var pos=0;

    if (typeof old != 'string') {
        text = self.replace(old, replace);
    } else {
        if (typeof count == 'number') do_max=true;

        while (start<self.length) {
            if (do_max && !count--) break;

            pos=self.indexOf(old, start);
            if (pos<0) break;

            text+=self.substring(start, pos) + replace;
            start=pos+old.length;
        }
        if (start<self.length) text+=self.substring(start);
    }
    return $new(@{{str}}, text);
});

func(${0,module}$, null, @{{str}}, '__contains__', ${0, function}$, ['self', 's'], null, null, null, function($self, $s) {
    return $self.valueOf().indexOf($s)>=0 ? @{{True}} : @{{False}};
});


func(${0,module}$, null, @{{str}}, 'split', ${0, function}$, ['self', 'sep', 'maxsplit'], null, null, [${0,None}$, ${0,None}$], function($self, $sep, $maxsplit) {
    var subject = $self.valueOf();
    var sep = $sep.valueOf();
    var maxsplit = $maxsplit.valueOf();
    var items=[];
    var do_max=false;
    var start=0;
    var pos=0;

    if (sep === null || typeof sep == "undefined") {
        sep=" ";
        if (subject.length == 0) {
            return $new(@{{list}}, items);
        }
        subject = subject.replace(/^\s+/, "");
        subject = subject.replace(/\s+$/, "");
        subject = subject.replace(/\s+/g, sep);
    }
    else if (typeof maxsplit == 'number') do_max=true;

    if (subject.length == 0) {
        items.push(B$str(''));
        return $new(@{{list}}, items);
    }

    while (start<subject.length) {
        if (do_max && !maxsplit--) break;

        pos=subject.indexOf(sep, start);
        if (pos<0) break;

        items.push(B$str(subject.substring(start, pos)));
        start=pos+sep.length;
    }
    if (start<=subject.length) items.push(B$str(subject.substring(start)));

    return $new(@{{list}}, items);
});

func(${0,module}$, null, @{{str}}, 'rsplit', ${0, function}$, ['self', 'sep', 'maxsplit'], null, null, [${0,None}$], function($self, $sep, $maxsplit) {
    var subject = self.valueOf();
    var sep = sep.valueOf();
    var maxsplit = maxsplit.valueOf();
    var items=[];
    var do_max=false;
    var pos=0;

    if (sep === null || typeof sep == "undefined") {
        sep=" ";
        if (subject.length == 0) {
            return $new(@{{list}}, items);
        }
        subject = subject.replace(/^\s+/, "");
        subject = subject.replace(/\s+$/, "");
        subject = subject.replace(/\s+/g, sep);
    }
    else if (typeof maxsplit == 'number') do_max=true;

    if (subject.length == 0) {
        items.push(B$str(''));
        return $new(@{{list}}, items);
    }

    while (subject.length > 0) {
        if (do_max && !maxsplit--) break;

        pos=subject.lastIndexOf(sep);
        if (pos<0) break;

        items.push(B$str(subject.substr(pos+sep.length)));
        subject = subject.substr(0, pos);
    }
    if (subject.length > 0) items.push(B$str(subject));
    items.reverse()

    return $new(@{{list}}, items);
});

if (typeof "a"[0] == "undefined" ) {
    // IE: cannot do "abc"[idx]
    func(${0,module}$, null, @{{str}}, '__iter__', ${0, function}$, ['self'], null, null, null, function(self) {
        self = self.valueOf();
        var i = 0;
        return {
            'next': function(noStop) {
                if (i >= self.length) {
                    if (noStop === true) {
                        return;
                    }
                    return @{{raise}}(@{{StopIteration}});
                }
                return $new(@{{str}}, self.charAt(i++));
            },
            '__iter__': function() {
                return this;
            }
        };
    });
} else {
    func(${0,module}$, null, @{{str}}, '__iter__', ${0, function}$, ['self'], null, null, null, function(self) {
        self = self.valueOf();
        var i = 0;
        return {
            '__array': self,
            'next': function(noStop) {
                if (i >= self.length) {
                    if (noStop === true) {
                        return;
                    }
                    return @{{raise}}(@{{StopIteration}});
                }
                return $new(@{{str}}, self.charAt(i++));
            },
            '__iter__': function() {
                return this;
            }
        };
    });
}

func(${0,module}$, null, @{{str}}, 'strip', ${0, function}$, ['self', 'chars'], null, null, [${0,None}$], function(self, chars) {
    self = self.valueOf();
    chars = chars.valueOf();
    if (typeof chars == 'string') {
        self = self.replace(new RegExp("^[" + chars + "]+"), "");
        self = self.replace(new RegExp("[" + chars + "]+$"), "");
    } else {
        self = self.replace(/^\s+/, "");
        self = self.replace(/\s+$/, "");
    }
    return $new(@{{str}}, self);
});

func(${0,module}$, null, @{{str}}, 'lstrip', ${0, function}$, ['self', 'chars'], null, null, [${0,None}$], function(self, chars) {
    self = self.valueOf();
    chars = chars.valueOf();
    if (typeof chars == 'string') {
        self = self.replace(new RegExp("^[" + chars + "]+"), "");
    } else {
        self = self.replace(/^\s+/, "");
    }
    return $new(@{{str}}, self);
});

func(${0,module}$, null, @{{str}}, 'rstrip', ${0, function}$, ['self', 'chars'], null, null, [${0,None}$], function(self, chars) {
    self = self.valueOf();
    chars = chars.valueOf();
    if (typeof chars == 'string') {
        self = self.replace(new RegExp("[" + chars + "]+$"), "");
    } else {
        self = self.replace(/\s+$/, "");
    }
    return $new(@{{str}}, self);
});

func(${0,module}$, null, @{{str}}, 'startswith', ${0, function}$, ['self', 'prefix', 'start', 'end'], null, null, [${0,None}$, ${0,None}$], function(self, prefix, start, end) {
    self = self.valueOf();
    prefix = prefix.valueOf();
    start = start.valueOf();
    end = end.valueOf();
    // FIXME: accept tuples as suffix (since 2.5)
    if (typeof start != 'number') start = 0;
    if (typeof end != 'number') end = self.length;

    if ((end - start) < prefix.length) return @{{False}};
    if (self.substr(start, prefix.length) == prefix) return @{{True}};
    return @{{False}};
});

func(${0,module}$, null, @{{str}}, 'endswith', ${0, function}$, ['self', 'suffix', 'start', 'end'], null, null, [${0,None}$, ${0,None}$], function(self, suffix, start, end) {
    self = self.valueOf();
    suffix = suffix.valueOf();
    start = start.valueOf();
    end = end.valueOf();
    // FIXME: accept tuples as suffix (since 2.5)
    if (typeof start != 'number') start = 0;
    if (typeof end != 'number') end = self.length;

    if ((end - start) < suffix.length) return @{{False}};
    if (self.substr(end - suffix.length, suffix.length) == suffix) return @{{True}};
    return @{{False}};
});

func(${0,module}$, null, @{{str}}, 'ljust', ${0, function}$, ['self', 'width', 'fillchar'], null, null, [${0,None}$, ${0,None}$], function($self, $width, $fillchar) {
    var s = $self.valueOf();
    fillchar = $fillchar.valueOf();
    width = $width.valueOf();
    switch ($width.__number__) {
        case 0x02:
        case 0x04:
            break;
        default:
            if (typeof width == 'number') {
                if (Math.floor(width) == width) break;
            }
        case 0x01:
            return @{{raise}}($new(@{{TypeError}}, B$str("an integer is required, got " + _typeof($width))));
    }
    if (typeof fillchar != 'string') fillchar = ' ';
    if (typeof(fillchar) != 'string' ||
        fillchar.length != 1) {
        return @{{raise}}($new(@{{TypeError}}, B$str("ljust() argument 2 must be char, not " + _typeof($fillchar))));
    }
    if (s.length >= width) return $self;
    return $new(@{{str}}, s + new Array(width+1 - s.length).join(fillchar));
});

func(${0,module}$, null, @{{str}}, 'rjust', ${0, function}$, ['self', 'width', 'fillchar'], null, null, [${0,None}$, ${0,None}$], function($self, $width, $fillchar) {
    var s = $self.valueOf();
    fillchar = $fillchar.valueOf();
    width = $width.valueOf();
    switch ($width.__number__) {
        case 0x02:
        case 0x04:
            break;
        default:
            if (typeof width == 'number') {
                if (Math.floor(width) == width) break;
            }
        case 0x01:
            return @{{raise}}($new(@{{TypeError}}, B$str("an integer is required, got " + _typeof($width))));
    }
    if (typeof fillchar != 'string') fillchar = ' ';
    if (typeof(fillchar) != 'string' ||
        fillchar.length != 1) {
        return @{{raise}}($new(@{{TypeError}}, B$str("rjust() argument 2 must be char, not " + _typeof($fillchar))));
    }
    if (s.length >= width) return $self;
    return B$str(new Array(width+1 - s.length).join(fillchar) + s);
});

func(${0,module}$, null, @{{str}}, 'center', ${0, function}$, ['self', 'width', 'fillchar'], null, null, [${0,None}$, ${0,None}$], function(self, width, fillchar) {
    var s = self.valueOf();
    fillchar = fillchar.valueOf();
    switch (width.__number__) {
        case 0x02:
        case 0x04:
            width = width.valueOf();
            break;
        case 0x01:
            if (Math.floor(width) == width) break;
        default:
            if (typeof width.valueOf() != 'number') {
                @{{raise}}($new(@{{TypeError}}, B$str("an integer is required (got '" + width + "')")));
            }
            width = width.valueOf();
    }
    if (typeof fillchar != 'string') fillchar = ' ';
    if (typeof(fillchar) != 'string' ||
        fillchar.length != 1) {
        @{{raise}}($new(@{{TypeError}}, B$str("center() argument 2 must be char, not " + typeof(fillchar))));
    }
    if (s.length >= width) return self;
    var padlen = width - s.length;
    var right = Math.ceil(padlen / 2);
    var left = padlen - right;
    return $new(@{{str}}, new Array(left+1).join(fillchar) + s + new Array(right+1).join(fillchar));
});


func(${0,module}$, null, @{{str}}, '__getslice__', ${0, function}$, ['self', 'lower', 'upper'], null, null, null, function($self, $lower, $upper) {
    var self = $self.valueOf();
    var lower = $lower.valueOf();
    var upper = $upper.valueOf();
    if (lower === null) {
        lower = 0;
    } else if (lower < 0) {
        lower = self.length + lower;
    }
    if (upper === null) {
        upper=self.length;
    } else if (upper < 0) {
       upper = self.length + upper;
    }
    return B$str(self.substring(lower, upper));
});

func(${0,module}$, null, @{{str}}, '__getitem__', ${0, function}$, ['self', 'idx'], null, null, null, function($self, $idx) {
    var self = $self.valueOf();
    var idx = $idx.valueOf()
    if (idx < 0) idx += self.length;
    if (idx < 0 || idx > self.length) {
        @{{raise}}($new(@{{IndexError}}, B$str("string index out of range")));
    }
    return B$str(self.charAt(idx));
});

func(${0,module}$, null, @{{str}}, '__setitem__', ${0, function}$, ['self', 'idx', 'val'], null, null, null, function(self, idx, val) {
    @{{raise}}($new(@{{TypeError}}, B$str("'str' object does not support item assignment")));
});

func(${0,module}$, null, @{{str}}, 'upper', ${0, function}$, ['self'], null, null, null, function(self) {
    var src = self.valueOf();
    var dst = src.toUpperCase();
    if (src == dst) {
        return self;
    }
    return $new(@{{str}}, dst);
});

func(${0,module}$, null, @{{str}}, 'lower', ${0, function}$, ['self'], null, null, null, function(self) {
    var src = self.valueOf();
    var dst = src.toLowerCase();
    if (src == dst) {
        return self;
    }
    return $new(@{{str}}, dst);
});

func(${0,module}$, null, @{{str}}, 'capitalize', ${0, function}$, ['self'], null, null, null, function(self) {
    var src = self.valueOf();
    var dst = src.charAt(0).toUpperCase() + src.substring(1);
    if (src == dst) {
        return self;
    }
    return $new(@{{str}}, dst);
});

func(${0,module}$, null, @{{str}}, 'zfill', ${0, function}$, ['self', 'width'], null, null, null, function($self, $width) {
    return @{{str}}['$dict']['rjust']($self, $width, '0');
});

func(${0,module}$, null, @{{str}}, '__mod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
    return @{{sprintf}}($self, $y);
});

func(${0,module}$, null, @{{str}}, '__add__', ${0, function}$, ['self', 'y'], null, null, null, function(self, y) {
    self = self.valueOf();
    y = y.valueOf();
    if (typeof y != "string") {
        @{{raise}}($new(@{{TypeError}}, B$str("cannot concatenate 'str' and non-str objects")));
    }
    return $new(@{{str}}, self + y);
});

func(${0,module}$, null, @{{str}}, '__mul__', ${0, function}$, ['self', 'y'], null, null, null, function(self, y) {
    self = self.valueOf();
    switch (y.__number__) {
        case 0x02:
        case 0x04:
            y = y.valueOf();
            break;
        case 0x01:
            if (Math.floor(y) == y) break;
        default:
            if (typeof y.valueOf() != 'number') {
                @{{raise}}($new(@{{TypeError}}, B$str("can't multiply sequence by non-int of type 'str'")));
            }
            y = y.valueOf();
    }
    var s = '';
    while (y-- > 0) {
        s += self;
    }
    return $new(@{{str}}, s);
});

func(${0,module}$, null, @{{str}}, '__str__', ${0, function}$, ['self'], null, null, null, function(self) {
    if (self.$inst === false) {
        return B$str("<type '" + self['$dict']['__name__'] + "'>");
    }
    return self;
});

func(${0,module}$, null, @{{str}}, '__repr__', ${0, function}$, ['self'], null, null, null, function(self) {
    if (self.$inst === false) {
        return B$str("<type '" + self['$dict']['__name__'] + "'>");
    }
    return B$str("'" + self.valueOf() + "'");
});

@{{str}}['$dict']['__rmul__'] = @{{str}}['$dict']['__mul__'];
""")



class float(object):

    JS("""
    ${0,funcbase}$['__slots__'] = [];
    func(${0,module}$, null, ${0,funcbase}$, '__new__', ${0, jsmethod}$, ['cls', 'value'], null, null, [0.0], function($cls, $value) {
        if ($value.__class__ === $cls) {
            return $value
        }
        var v = $value.valueOf();
        if (typeof v == 'string') {
            v = v.replace(/^\s+/, "");
            if (v === "") {
                @{{raise}}($new(@{{ValueError}}, B$str("empty string for float()")));
            }
            v = Number(v);
        } else if (typeof v != 'number') {
            @{{raise}}($new(@{{TypeError}}, B$str("float() argument must be a string or a number")));
        }
        if (isNaN(v)) {
            @{{raise}}($new(@{{ValueError}}, B$str("invalid literal for float(): " + $value.valueOf())));
        }
${1, __new__, instance, @{{float}}}$
        instance['__r'] = v; // real
        //instance['__i'] = 0.0; // imag
        instance['__number__'] = 0x01;
        instance['valueOf'] = function() {return this.__r;};
        instance['toString'] = function() {return this.__r.toString();};
        return instance;
    });
    func(${0,module}$, null, ${0,funcbase}$, '__abs__', ${0, function}$, ['self'], null, null, null, function($self) {
        var v = $self.valueOf();
        return v < 0 ? $new(@{{float}}, -v) : $self;
    });
    func(${0,module}$, null, ${0,funcbase}$, '__add__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $new(@{{float}}, $self + $y);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__div__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $new(@{{float}}, Math.floor($self + $y));
    });
    func(${0,module}$, null, ${0,funcbase}$, '__divmod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        if ($y == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("float divmod()")));
        var f = Math.floor($self / $y);
        return $new(@{{tuple}}, [$new(@{{float}}, f), $new(@{{float}}, $self - f * $y)]);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__eq__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $self == y ? @{{True}} : @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__floordiv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        if ($y == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("float divmod()")));
        $self /= $y;
        return $new(@{{float}}, $self < 0 ? Math.ceil($self) : Math.floor($self));
    });
    func(${0,module}$, null, ${0,funcbase}$, '__ge__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $self >= y ? @{{True}} : @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__gt__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $self > y ? @{{True}} : @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__hash__', ${0, function}$, ['self'], null, null, null, function($self) {
        return "#float#" + self.valueOf();
    });
    func(${0,module}$, null, ${0,funcbase}$, '__int__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $new(@{{int}}, $self.valueOf());
    });
    func(${0,module}$, null, ${0,funcbase}$, '__le__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $self <= y ? @{{True}} : @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__long__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $new(@{{long}}, $self.valueOf());
    });
    func(${0,module}$, null, ${0,funcbase}$, '__lt__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $self < y ? @{{True}} : @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__mod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $new(@{{float}}, $self % $y);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__mul__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $new(@{{float}}, $self * $y);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__ne__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $self != y ? @{{True}} : @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__neg__', ${0, function}$, ['self'], null, null, null, function($self) {
        $self = $self.valueOf();
        return $new(@{{float}}, -$self);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__nonzero__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $self.valueOf() == 0 ? @{{False}} : @{{True}};
    });
    func(${0,module}$, null, ${0,funcbase}$, '__pos__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $new(@{{float}}, $self);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__pow__', ${0, function}$, ['self', 'y', 'z'], null, null, [null], function($self, $y, $z) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        if (typeof $z == "undefined" || $z == null) {
            return $new(@{{float}}, Math.pow($self,  $y));
        }
        $z = $z.valueOf();
        if (isNaN(z)) return @{{NotImplemented}};
        return $new(@{{float}}, Math.pow($self, $) % $z);
    });
    /*
    func(${0,module}$, null, ${0,funcbase}$, '__radd__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, '__rdiv__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    */
    func(${0,module}$, null, ${0,funcbase}$, '__rdivmod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        if ($y == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("float divmod()")));
        var f = Math.floor($y / $self);
        return $new(@{{tuple}}, [$new(@{{float}}, f), $new(@{{float}}, $y - f * $self)]);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
        $self = $self.valueOf();
        return $new(@{{str}}, $self.toString());
    });
    /*
    func(${0,module}$, null, ${0,funcbase}$, '__rfloordiv__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, '__rmod__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, '__rmul__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, '__rpow__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, '__rsub__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, '__rtruediv__', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    */
    func(${0,module}$, null, ${0,funcbase}$, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str($self.valueOf().toString());
    });
    func(${0,module}$, null, ${0,funcbase}$, '__sub__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $new(@{{float}}, $self - $y);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__truediv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        $self = $self.valueOf();
        $y = $y.valueOf();
        if (typeof $y != 'number') {
            return @{{NotImplemented}};
        }
        return $new(@{{float}}, $self / $y);
    });
    func(${0,module}$, null, ${0,funcbase}$, '__trunc__', ${0, function}$, ['self'], null, null, null, function($self) {
        $self = $self.valueOf();
        return $new(@{{float}}, $self < 0 ? Math.ceil($self) : Math.floor($self));
    });
    /*
    func(${0,module}$, null, ${0,funcbase}$, 'as_integer_ratio', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, 'conjugate', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, 'fromhex', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    func(${0,module}$, null, ${0,funcbase}$, 'hex', ${0, function}$, ['self'], null, null, null, function($self) {
    });
    */
    func(${0,module}$, null, ${0,funcbase}$, 'imag', ${0, function}$, ['self'], null, null, null, function($self) {
        return $new(@{{float}}, 0.0);
    });
    func(${0,module}$, null, ${0,funcbase}$, 'is_integer', ${0, function}$, ['self'], null, null, null, function($self) {
        $self = $self.valueOf();
        if ($self == Math.floor($self) || $self == Math.ceil($self)) {
            return @{{True}};
        }
        return @{{False}};
    });
    func(${0,module}$, null, ${0,funcbase}$, 'real', ${0, function}$, ['self'], null, null, null, function($self) {
        return $new(@{{float}}, self.__r);
    });""")


class int(object):
    # Some dummy defines to enable the translator to
    # refer to these at the end (e.g. __div__ = __floordiv__)
    __floordiv__ = __rfloordiv__ = None
    JS("""
    ${0,funcbase}$['__slots__'] = [];
    func(${0,module}$, null, ${0,funcbase}$, '__new__', ${0, jsmethod}$, ['cls', 'value', 'radix'], null, null, [null], function(cls, value, radix) {
        var i, v = value.valueOf();
${2, __new__, new_int, cls}$
        new_int['__v'] =  v;
        new_int['__number__'] =  0x02,
        new_int['valueOf'] = function() {return this.__v;};
        new_int['toString'] = function() {return this.__v.toString();};
        if (radix === null || typeof radix == "undefined") {
            // handle some javascript types
            switch (typeof value) {
                case 'number':
                    v = v > 0 ? Math.floor(v) : Math.ceil(v);
                    if (v == -1) {
                        return @{{int_minus_one}};
                    } else if (v == 0) {
                        return @{{int_zero}};
                    } else if (v == 1) {
                        return @{{int_one}};
                    }
                    new_int['__v'] =  v;
                    return new_int;
                case 'boolean':
                    return v ? @{{int_one}} : @{{int_zero}};
                case "undefined":
                    @{{raise}}($new(@{{TypeError}}, B$str("int() takes at least 1 argument")));
            }
            // TODO: change usage of value.__number__
            // to value.__class__
            switch (value.__number__) {
                case 0x01: // float
                    v = v > 0 ? Math.floor(v) : Math.ceil(v);
                    if (v == -1) {
                        return @{{int_minus_one}};
                    } else if (v == 0) {
                        return @{{int_zero}};
                    } else if (v == 1) {
                        return @{{int_one}};
                    }
                    new_int['__v'] =  v;
                    return new_int;
                    break;
                case 0x02: // int
                    return value;
                case 0x04: // long
                    if (!($min_int <= v && v <= $max_int))
                        return value;
                    if (v == -1) {
                        return @{{int_minus_one}};
                    } else if (v == 0) {
                        return @{{int_zero}};
                    } else if (v == 1) {
                        return @{{int_one}};
                    }
                    return new_int;
                default:
                    if (v === 'true') {
                        return @{{int_one}};
                    } else if (v === 'true') {
                        return @{{int_zero}};
                    }
                    // check whether we've got an object that has its own __int__() method
                    var mro$;
${6, getattribute, mro$, i, value, '__int__'}$
                    if (typeof i != "undefined") {
                        return mcall(cls, null, value, '__call__');
                    }
            }
            // now we've converted numbers, so this should be a string
            if (typeof v != 'string') {
                var msg = "int() argument must be a string or a number";
                if (typeof value['__class__'] != "undefined") {
                    msg = msg + ", not '" + value['__class__']['__name__'] + "'";
                }
                @{{raise}}($new(@{{TypeError}}, B$str(msg)));
            }
            radix = 10;
        } else if (typeof v != 'string') {
            @{{raise}}($new(@{{TypeError}}, B$str("int() can't convert non-string with explicit base")));
        } else {
            if (radix.__class__ === @{{int}}) {
                radix = radix.valueOf();
            } else if (radix.__class__ === @{{bool}}) {
                radix = @{{bool}} === @{{True}} ? 1 : 0;
            } else if (radix.__class__ === @{{long}}) {
                radix = radix.valueOf();
            } else {
                @{{raise}}($new(@{{TypeError}}, B$str("an integer is required")));
            }
            if (radix < 2 || radix > 36) {
                @{{raise}}($new(@{{ValueError}}, B$str("int() base must be >= 2 and <= 36")));
            }
        }
        if (v.match($radix_regex[radix]) === null) {
            v = NaN;
        } else {
            v = v.replace(' ', '');
            v = parseInt(v, radix);
        }
        if (isNaN(v) || !isFinite(v)) {
            v = value.valueOf().replace(/^\s+/, "");
            @{{raise}}($new(@{{ValueError}}, B$str("invalid literal for int() with base " + radix + ": '" + v + "'")));
        }
        if ($min_int <= v && v <= $max_int) {
            new_int.__v = v;
            return new_int;
        }
        return @{{long}}(v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__init__', ${0, function}$, ['self', 'value', 'radix'], null, null, [${0,None}$], function($self, $value, $radix) {
        return ${0,None}$;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
        if ($self['$inst'] === true) return B$str($self.__v.toString());
        if ($self['$inst'] === false) return B$str("<type '" + $self['__name__'] + "'>");
        return B$str($self.toString());
    });

    func(${0,module}$, null, ${0,funcbase}$, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
        if ($self['$inst'] === true) return B$str($self.__v.toString());
        if ($self['$inst'] === false) return B$str("<type '" + $self['__name__'] + "'>");
        return B$str($self.toString());
    });

    func(${0,module}$, null, ${0,funcbase}$, '__nonzero__', ${0, function}$, ['self'], null, null, null, function($self) {
        return __v == 0 ? @{{False}} : @{{True}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__hash__', ${0, function}$, ['self'], null, null, null, function($self) {
        return '#int#' + $self.__v;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__invert__', ${0, function}$, ['self'], null, null, null, function($self) {
        $new(@{{int}}, ~$self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__cmp__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        return $self < $y ? @{{int_minus_one}} : ($self == $y ? @{{int_zero}} : @{{int_one}});
    });

    func(${0,module}$, null, ${0,funcbase}$, '__lshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($y < 32) {
            var v = $self.__v << $y;
            if (v > $self.__v) {
                return $new(@{{int}}, v);
            }
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__lshift__'](v, $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rlshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($self.__v < 32) {
            var v = $y << $self.__v;
            if (v > $self.__v) {
                return $new(@{{int}}, v);
            }
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__rlshift__'](v, $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        return $new(@{{int}}, $self.__v >> $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rrshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        return $new(@{{int}}, $y >> $self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__and__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ == 0x02) {
            $y = $y.__v;
        } else if (!_isJsInt($y)) {
            return @{{NotImplemented}};
        }
        return $new(@{{int}}, $self.__v & $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rand__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ == 0x02) {
            $y = $y.__v;
        } else if (!_isJsInt($y)) {
            return @{{NotImplemented}};
        }
        return $new(@{{int}}, $y & $self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__xor__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ == 0x02) {
            $y = $y.__v;
        } else if (!_isJsInt($y)) {
            return @{{NotImplemented}};
        }
        return $new(@{{int}}, $self.__v ^ $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rxor__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ == 0x02) {
            $y = $y.__v;
        } else if (!_isJsInt($y)) {
            return @{{NotImplemented}};
        }
        return $new(@{{int}}, $y ^ $self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__or__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ == 0x02) {
            $y = $y.__v;
        } else if (!_isJsInt($y)) {
            return @{{NotImplemented}};
        }
        return $new(@{{int}}, $self.__v | $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__ror__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ == 0x02) {
            $y = $y.__v;
        } else if (!_isJsInt($y)) {
            return @{{NotImplemented}};
        }
        return $new(@{{int}}, $y | $self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__oct__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str('0x'+$self.__v.toString(8));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__hex__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str('0x'+$self.__v.toString(16));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__pos__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $self;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__neg__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $new(@{{int}}, -$self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__abs__', ${0, function}$, ['self'], null, null, null, function($self) {
        if ($self.__v >= 0) return $self;
        return $new(@{{int}}, -$self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__add__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = $self.__v + $y;
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__add__'](v, $new(@{{long}}, $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__radd__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = $self.__v + $y;
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__radd__'](v, $new(@{{long}}, $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__sub__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = $self.__v - $y;
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__sub__'](v, $new(@{{long}}, $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rsub__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = $y -$self.__v;
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__rsub__'](v, $new(@{{long}}, $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__floordiv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($y == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo b$y zero")));
        return $new(@{{int}}, Math.floor($self.__v / $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rfloordiv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($self.__v == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo b$y zero")));
        return $new(@{{int}}, Math.floor($y / $self.__v));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__truediv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($y == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo b$y zero")));
        $y = $self.__v / $y
        if ($y == Math.floor($y)) {
            return $new(@{{int}}, $y);
        }
        return $new(@{{float}}, $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rtruediv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($self.__v == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo b$y zero")));
        $y = $y / $self.__v
        if ($y == Math.floor($y)) {
            return $new(@{{int}}, $y);
        }
        return $new(@{{float}}, $y);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__mul__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = $self.__v * $y;
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__mul__'](v, $new(@{{long}}, $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rmul__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = $self.__v * $y;
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__rmul__'](v, $new(@{{long}}, $y));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__mod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($y == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo b$y zero")));
        var v = $self.__v % $y;
        return $new(@{{int}}, v < 0 && $y > 0 ? v + $y : v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rmod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        if ($self.__v == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo b$y zero")));
        return $new(@{{int}}, $y % $self.__v);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__pow__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        if ($y.__number__ != 0x02) return @{{NotImplemented}};
        $y = $y.__v;
        var v = Math.pow($self.__v, $y);
        if ($min_int <= v && v <= $max_int) {
            return $new(@{{int}}, v);
        }
        if (-$max_float_int < v && v < $max_float_int) {
            return $new(@{{long}}, v);
        }
        var v = $new(@{{long}}, $self.__v);
        return @{{long}}['$dict']['__pow__'](v, $new(@{{long}}, $y));
    });
    """)
    __div__ = __floordiv__
    __rdiv__ = __rfloordiv__


JS("""
init_short_names$(true);
@{{int_minus_one}} = $new(@{{int}}, 100);
@{{int_zero}} = $new(@{{int}}, 100);
@{{int_one}} = $new(@{{int}}, 100);
@{{int_minus_one}}.__v = -1;
@{{int_zero}}.__v = 0;
@{{int_one}}.__v = 1;
@{{int_max_index}} = $new(@{{int}}, 2147483647);
@{{max_int}} = $new(@{{int}}, 0x7fffffff);
@{{min_int}} = $new(@{{int}}, -0x80000000);

""")


class bool(int):
    JS("""
    ${0,funcbase}$['__slots__'] = [];
    func(${0,module}$, null, ${0,funcbase}$, '__new__', ${0, jsmethod}$, ['cls', 'value'], null, null, [false], function($cls, $value) {
${2, new_bool, $value, @{{True}}, @{{False}}}$
    });""")

    def __init__(self, value):
        pass

    def __str__(self):
        if self is True:
            return "True"
        if self is False:
            return "False"
        if JS("@{{self}}.$inst === true"):
            if JS("@{{self}}.__v"):
                return "True"
            else:
                return "False"
        JS("""if (@{{self}}.$inst === false) return B$str("<type '" + @{{self}}.__name__ + "'>");
        return B$str(@{{self}}.toString());""")

    __repr__ = __str__

JS("init_short_names$(true);")
JS("""
@{{False}} = $new(@{{int}}, 100);
@{{True}} = $new(@{{int}}, 100);
@{{False}}.__class__ = @{{bool}};
@{{True}}.__class__ = @{{bool}};
@{{False}}.__v = 0;
@{{True}}.__v = 1;
@{{False}}['toString'] = function () {return 'False'};
@{{True}}['toString'] = function () {return 'True'};
""")
JS("init_short_names$(true);")


class long(object):
    pass
# This is the python long implementation. See:
#  - Include/longintrepr.h
#  - Include/longobject.h
#  - Objects/longobject.c
JS("""
(function() {

    var $log2 = Math.log(2);
    var $DigitValue = [
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  37, 37, 37, 37, 37, 37,
            37, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
            25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 37, 37, 37, 37,
            37, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
            25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
            37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37
    ];
    var $log_base_PyLong_BASE = new Array();
    var $convwidth_base = new Array();
    var $convmultmax_base = new Array();
    for (var i = 0; i < 37; i++) {
        $log_base_PyLong_BASE[i] = $convwidth_base[i] = $convmultmax_base[i] = 0;
    }
    var $cdigit = '0123456789abcdefghijklmnopqrstuvwxyz';


    var PyLong_SHIFT = 15;
    var PyLong_MASK = 0x7fff;
    var PyLong_BASE = 0x8000;

    var KARATSUBA_CUTOFF = 70;
    var KARATSUBA_SQUARE_CUTOFF = (2 * KARATSUBA_CUTOFF);

    var FIVEARY_CUTOFF = 8;

    function array_eq(a, b, n) {
        for (var i = 0 ; i < n; i++) {
            if (a[i] != b[i])
                return false;
        }
        return true;
    }

    function long_normalize(v) {
        var j = v.ob_size < 0 ? -v.ob_size:v.ob_size;
        var i = j;
        while (i > 0 && v.ob_digit[i-1] == 0) {
            i--;
        }
        if (i != j) {
            v.ob_size = v.ob_size < 0 ? -i:i;
        }
        return v;
    }

    function AsScaledDouble(vv) {
        var multiplier = PyLong_BASE; // 1L << PyLong_SHIFT == 1 << 15
        var neg, i, x, nbitsneeded;

        if (vv.ob_size < 0) {
            i = -vv.ob_size;
            neg = true;
        } else if (vv.ob_size > 0) {
            i = vv.ob_size;
            neg = false;
        } else {
            return [0.0, 0];
        }
        --i;
        x = vv.ob_digit[i];
        nbitsneeded = 56;
        while (i > 0 && nbitsneeded > 0) {
            --i;
            x = x * multiplier + vv.ob_digit[i];
            nbitsneeded -= PyLong_SHIFT;
        }
        if (neg) {
            return [-x, i];
        }
        return [x, i];
    }

    function v_iadd(x, m, y, n) {
        var i, carry = 0;
        for (i = 0; i < n; ++i) {
                carry += x[i] + y[i];
                x[i] = carry & PyLong_MASK;
                carry >>= PyLong_SHIFT;
        }
        for (; carry && i < m; ++i) {
                carry += x[i];
                x[i] = carry & PyLong_MASK;
                carry >>= PyLong_SHIFT;
        }
        return carry;
    }

    function v_isub(x, m, y, n) {
        var i, borrow = 0;
        for (i = 0; i < n; ++i) {
                borrow = x[i] - y[i] - borrow;
                x[i] = borrow & PyLong_MASK;
                borrow >>= PyLong_SHIFT;
                borrow &= 1;
        }
        for (; borrow && i < m; ++i) {
                borrow = x[i] - borrow;
                x[i] = borrow & PyLong_MASK;
                borrow >>= PyLong_SHIFT;
                borrow &= 1;
        }
        return borrow;
    }

    //function mul1(a, n) {
    //    return muladd1(a, n, 0);
    //}

    function muladd1(z, a, n, extra) {
        var size_a = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        var carry = extra, i;

        for (i = 0; i < size_a; ++i) {
                carry += a.ob_digit[i] * n;
                z.ob_digit[i] = carry & PyLong_MASK;
                carry >>= PyLong_SHIFT;
        }
        z.ob_digit[i] = carry;
        z.ob_size = i + 1;
        return long_normalize(z);
    }

    function inplace_divrem1(pout, pin, pout_idx, pin_idx, size, n) {
        var rem = 0, hi = 0;
        pin_idx += size;
        pout_idx += size;
        while (pin_idx > pin.length) {
            --size;
            --pin_idx;
            pout[--pout_idx] = 0;
        }
        while (--size >= 0) {
            rem = (rem << PyLong_SHIFT) + pin[--pin_idx];
            pout[--pout_idx] = hi = Math.floor(rem / n);
            rem -= hi * n;
        }
        return [rem, pout_idx, pin_idx];
    }

    function divrem1(a, n, prem) {
        var size = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        var z = $new(@{{long}}, 0);

        prem[0] = inplace_divrem1(z.ob_digit, a.ob_digit, 0, 0, size, n)[0];
        z.ob_size = size;
        return long_normalize(z);
    }

    function Format(aa, base, addL, newstyle, noBase) {
        var text, str, p, i, bits, sz, rem, sign = '';
        var c_0 = "0".charCodeAt(0);
        var c_a = "a".charCodeAt(0);
        base = base.valueOf();

        if (aa.ob_size == 0) {
            if (addL) {
                text = "0L";
            } else {
                text = "0";
            }
        } else {
            if (aa.ob_size < 0) {
                sign = '-';
                size_a = -aa.ob_size;
            } else {
                size_a = aa.ob_size;
            }
            i = base;
            bits = 0;
            while (i > 1) {
                ++bits;
                i >>>= 1;
            }
            i = addL ? 6 : 5;
            j = size_a * PyLong_SHIFT + bits - 1;
            sz = Math.floor(i + j / bits);
            if (j / PyLong_SHIFT < size_a || sz < i) {
                @{{raise}}($new(@{{OverflowError}}, B$str("long is too large to format")));
            }
            str = new Array();
            p = sz;
            if (addL) str[--p] = 'L';
            if ((base & (base - 1)) == 0) {
                var accum = 0, accumbits = 0, basebits = 1;
                i = base;
                while ((i >>>= 1) > 1) ++basebits;
                for (i = 0; i < size_a; ++i) {
                    accum |= aa.ob_digit[i] << accumbits;
                    accumbits += PyLong_SHIFT;
                    while (1) {
                        var cdigit = accum & (base - 1);
                        str[--p] = $cdigit.charAt(cdigit);
                        accumbits -= basebits;
                        accum >>>= basebits;
                        if (i < size_a-1) {
                            if (accumbits < basebits) break;
                        } else if (accum <= 0) break;
                    }
                }
                text = str.join("");
            } else {
                // Not 0, and base not a power of 2.
                var scratch, pin, scratch_idx, pin_idx;
                var powbase = base, power = 1, size = size_a;

                while (1) {
                    var newpow = powbase * base;
                    if (newpow >>> PyLong_SHIFT)  /* doesn't fit in a digit */
                        break;
                    powbase = newpow;
                    ++power;
                }
                scratch = aa.ob_digit.slice(0);
                pin = aa.ob_digit;
                scratch_idx = pin_idx = 0;
                do {
                        var ntostore = power;
                        rem = inplace_divrem1(scratch, pin, scratch_idx, pin_idx, size, powbase);
                        scratch_idx = rem[1];
                        rem = rem[0];
                        pin = scratch;
                        pin_idx = 0;
                        if (pin[size - 1] == 0) {
                            --size;
                        }
                        do {
                            var nextrem = Math.floor(rem / base);
                            str[--p] = $cdigit.charAt(rem - nextrem * base);
                            rem = nextrem;
                            --ntostore;
                        } while (ntostore && (size || rem));
                } while (size !=0);
                text = str.slice(p).join("");
            }
            text = text.replace(/^\s+/, ""); // lstrip
            if (text == "" || text == "L") text = "0" + text;
        }
        if (noBase !== false) {
            switch (base) {
                case 10:
                    break;
                case 2:
                    text = '0b' + text;
                    break;
                case 8:
                    text = (newstyle ? '0o':(aa.ob_size ? '0': '')) + text;
                    break;
                case 16:
                    text = '0x' + text;
                    break;
                default:
                    text = base + '#' + text;
                    break;
            }
        }
        return sign + text;
    }

    function long_divrem(a, b, pdiv, prem) {
        var size_a = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        var size_b = b.ob_size < 0 ? -b.ob_size : b.ob_size;
        var z = null;

        if (size_b == 0) {
            @{{raise}}($new(@{{ZeroDivisionError}}, B$str("long division or modulo by zero")));
        }
        if (size_a < size_b ||
            (size_a == size_b &&
             a.ob_digit[size_a-1] < b.ob_digit[size_b-1])) {
                // |a| < |b|
                pdiv.ob_size = 0;
                prem.ob_digit = a.ob_digit.slice(0);
                prem.ob_size = a.ob_size;
                return 0;
        }
        if (size_b == 1) {
                rem = [0];
                prem.ob_digit = [0];
                prem.ob_size = 1;
                z = divrem1(a, b.ob_digit[0], prem.ob_digit);
                prem = long_normalize(prem);
        }
        else {
                z = @{{!x_divrem}}(a, b, prem);
        }
        if (z === null) {
            pdiv.ob_size = 0;
        } else {
            pdiv.ob_digit = z.ob_digit.slice(0);
            pdiv.ob_size = z.ob_size;
        }
        if ((a.ob_size < 0) != (b.ob_size < 0))
                pdiv.ob_size = -(pdiv.ob_size);
        if (a.ob_size < 0 && prem.ob_size != 0)
                prem.ob_size = -prem.ob_size;
        return 0;
    }

    function x_divrem(v1, w1, prem) {
        var size_w = w1.ob_size < 0 ? -w1.ob_size : w1.ob_size;
        var d = Math.floor(PyLong_BASE / (w1.ob_digit[size_w-1] + 1));
        var v = muladd1($x_divrem_v, v1, d, 0);
        var w = muladd1($x_divrem_w, w1, d, 0);
        var a, j, k;
        var size_v = v.ob_size < 0 ? -v.ob_size : v.ob_size;
        k = size_v - size_w;
        a = $new(@{{long}}, 0);
        a.ob_size = k + 1;

        for (j = size_v; k >= 0; --j, --k) {
            var vj = (j >= size_v) ? 0 : v.ob_digit[j];
            var carry = 0;
            var q, i;

            if (vj == w.ob_digit[size_w-1])
                q = PyLong_MASK;
            else
                q = Math.floor(((vj << PyLong_SHIFT) + v.ob_digit[j-1]) /
                        w.ob_digit[size_w-1]);

            while (w.ob_digit[size_w-2]*q >
                    ((
                        (vj << PyLong_SHIFT)
                        + v.ob_digit[j-1]
                        - q*w.ob_digit[size_w-1]
                                                ) << PyLong_SHIFT)
                    + v.ob_digit[j-2])
                --q;

            for (i = 0; i < size_w && i+k < size_v; ++i) {
                var z = w.ob_digit[i] * q;
                var zz = z >>> PyLong_SHIFT;
                carry += v.ob_digit[i+k] - z
                        + (zz << PyLong_SHIFT);
                v.ob_digit[i+k] = carry & PyLong_MASK;
                // carry = Py_ARITHMETIC_RIGHT_SHIFT(BASE_TWODIGITS_TYPE, carry, PyLong_SHIFT);
                carry >>= PyLong_SHIFT;
                carry -= zz;
            }

            if (i+k < size_v) {
                carry += v.ob_digit[i+k];
                v.ob_digit[i+k] = 0;
            }

            if (carry == 0)
                a.ob_digit[k] = q;
            else {
                a.ob_digit[k] = q-1;
                carry = 0;
                for (i = 0; i < size_w && i+k < size_v; ++i) {
                    carry += v.ob_digit[i+k] + w.ob_digit[i];
                    v.ob_digit[i+k] = carry & PyLong_MASK;
                    // carry = Py_ARITHMETIC_RIGHT_SHIFT( BASE_TWODIGITS_TYPE, carry, PyLong_SHIFT);
                    carry >>= PyLong_SHIFT;
                }
            }
        } /* for j, k */

        i = divrem1(v, d, prem);
        prem.ob_digit = i.ob_digit.slice(0);
        prem.ob_size = i.ob_size;
        return long_normalize(a);
    }

    function x_add(a, b) {
        var size_a = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        var size_b = b.ob_size < 0 ? -b.ob_size : b.ob_size;
        var z = $new(@{{long}}, 0);
        var i;
        var carry = 0;

        if (size_a < size_b) {
            var temp = a;
            a = b;
            b = temp;
            temp = size_a;
            size_a = size_b;
            size_b = temp;
        }
        for (i = 0; i < size_b; ++i) {
                carry += a.ob_digit[i] + b.ob_digit[i];
                z.ob_digit[i] = carry & PyLong_MASK;
                carry >>>= PyLong_SHIFT;
        }
        for (; i < size_a; ++i) {
                carry += a.ob_digit[i];
                z.ob_digit[i] = carry & PyLong_MASK;
                carry >>>= PyLong_SHIFT;
        }
        z.ob_digit[i] = carry;
        z.ob_size = i+1;
        return long_normalize(z);
    }

    function x_sub(a, b) {
        var size_a = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        var size_b = b.ob_size < 0 ? -b.ob_size : b.ob_size;
        var z = $new(@{{long}}, 0);
        var i;
        var borrow = 0;
        var sign = 1;

        if (size_a < size_b) {
            var temp = a;
            a = b;
            b = temp;
            temp = size_a;
            size_a = size_b;
            size_b = temp;
            sign = -1;
        } else if (size_a == size_b) {
            i = size_a;
            while (--i >= 0 && a.ob_digit[i] == b.ob_digit[i])
                ;
            if (i < 0)
                return z;
            if (a.ob_digit[i] < b.ob_digit[i]) {
                var temp = a;
                a = b;
                b = temp;
                temp = size_a;
                size_a = size_b;
                size_b = temp;
                sign = -1;
            }
            size_a = size_b = i+1;
        }
        for (i = 0; i < size_b; ++i) {
                borrow = a.ob_digit[i] - b.ob_digit[i] - borrow;
                z.ob_digit[i] = borrow & PyLong_MASK;
                borrow >>>= PyLong_SHIFT;
                borrow &= 1;
        }
        for (; i < size_a; ++i) {
                borrow = a.ob_digit[i] - borrow;
                z.ob_digit[i] = borrow & PyLong_MASK;
                borrow >>>= PyLong_SHIFT;
                borrow &= 1;
        }
        z.ob_size = i;
        if (sign < 0)
            z.ob_size = -(z.ob_size);
        return long_normalize(z);
    }

    function x_mul(a, b) {
        var size_a = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        var size_b = b.ob_size < 0 ? -b.ob_size : b.ob_size;
        var z = $new(@{{long}}, 0);
        var i, s;

        z.ob_size = size_a + size_b;
        for (i = 0; i < z.ob_size; i++) {
            z.ob_digit[i] = 0;
        }
        if (size_a == size_b && array_eq(a.ob_digit, b.ob_digit, size_a)) {
            // Efficient squaring per HAC, Algorithm 14.16:
            for (i = 0; i < size_a; ++i) {
                var carry;
                var f = a.ob_digit[i];
                var pz = (i << 1);
                var pa = i + 1;
                var paend = size_a;

                carry = z.ob_digit[pz] + f * f;
                z.ob_digit[pz++] = carry & PyLong_MASK;
                carry >>>= PyLong_SHIFT;

                f <<= 1;
                while (pa < paend) {
                    carry += z.ob_digit[pz] + a.ob_digit[pa++] * f;
                    z.ob_digit[pz++] = carry & PyLong_MASK;
                    carry >>>= PyLong_SHIFT;
                }
                if (carry) {
                    carry += z.ob_digit[pz];
                    z.ob_digit[pz++] = carry & PyLong_MASK;
                    carry >>>= PyLong_SHIFT;
                }
                if (carry) {
                    z.ob_digit[pz] += carry & PyLong_MASK;
                }
            }
        }
        else {  // a is not the same as b -- gradeschool long mult
            for (i = 0; i < size_a; ++i) {
                var carry = 0;
                var f = a.ob_digit[i];
                var pz = i;
                var pb = 0;
                var pbend = size_b;

                while (pb < pbend) {
                    carry += z.ob_digit[pz] + b.ob_digit[pb++] * f;
                    z.ob_digit[pz++] = carry & PyLong_MASK;
                    carry >>>= PyLong_SHIFT;
                }
                if (carry) {
                    z.ob_digit[pz] += carry & PyLong_MASK;
                }
            }
        }
        z.ob_size = z.ob_digit.length;
        return long_normalize(z);
    }

    function l_divmod(v, w, pdiv, pmod) {
        var div = $l_divmod_div,
            mod = $l_divmod_mod;

        if (long_divrem(v, w, div, mod) < 0)
                return -1;
        if (pdiv == null && pmod == null) return 0;

        if ((mod.ob_size < 0 && w.ob_size > 0) ||
            (mod.ob_size > 0 && w.ob_size < 0)) {
                mod = @{{long}}['$dict']['__add__'](mod, w);
                div = @{{long}}['$dict']['__sub__'](div, $const_long_1);
        }
        if (pdiv !== null) {
            pdiv.ob_digit = div.ob_digit.slice(0);
            pdiv.ob_size = div.ob_size;
        }
        if (pmod !== null) {
            pmod.ob_digit = mod.ob_digit.slice(0);
            pmod.ob_size = mod.ob_size;
        }
        return 0;
    }


    // class long

    var ${0,funcbase}$ = @{{long}};
    ${0,funcbase}$['__slots__'] = [];

    func(${0,module}$, null, ${0,funcbase}$, '__new__', ${0, jsmethod}$, ['cls', 'value', 'radix'], null, null, [null], function(cls, value, radix) {
        var v = value.valueOf(), i;
        var new_long = B$__new__(@{{long}});
        new_long['__number__'] = 0x04;
        new_long['valueOf'] = function() {
                var x, v;
                x = AsScaledDouble(this);
                // ldexp(a,b) == a * (2**b)
                v = x[0] * Math.pow(2.0, x[1] * PyLong_SHIFT);
                return v;
            };
        new_long['toString'] = function() {return this['__class__']['$dict']['__str__'](this)};
        if (!radix || radix.valueOf() == 0) {
            if (typeof value == "undefined") {
               @{{raise}}($new(@{{TypeError}}, B$str("long() takes at least 1 argument")));
            }
            // handle some javascript types
            switch (typeof value) {
                case 'number':
                    v = v > 0 ? Math.floor(v) : Math.ceil(v);
                    break;
                case 'string':
                    break;
                case 'boolean':
                    return v ? @{{long_one}} : @{{long_zero}};
                case "undefined":
                    @{{raise}}($new(@{{TypeError}}, B$str("long() takes at least 1 argument")));
                default:
                // TODO: change usage of value.__number__
                // to value.__class__
                switch (value.__number__) {
                    case 0x01: // float
                        v = v > 0 ? Math.floor(v) : Math.ceil(v);
                        break;
                    case 0x02: // int
                        break;
                    case 0x04: // long
                        return value;
                    default:
                        if (v === 'true') {
                            return @{{long_one}};
                        } else if (v === 'true') {
                            return @{{long_zero}};
                        }
/*
                        // check whether we've got an object that has its own __long__() method
                        var mro$;
${7, getattribute, mro$, i, value, '__long__'}$
                        if (typeof i != "undefined") {
                            return mcall(cls, null, value, '__call__');
                        }
*/
                }
            }
            radix = null;
        }

        new_long.ob_size = 0;
        new_long.ob_digit = new Array();
        if (typeof v == 'number') {
            if (radix) {
                @{{raise}}($new(@{{TypeError}}, B$str("long() can't convert non-string with explicit base")));
            }
            if ($min_int <= v && v <= $max_int) {
                var neg = false;
                var ndig = 0;

                if (v < 0) {
                    v = -v;
                    neg = true;
                }
                // Count the number of Python digits.
                var t = v;
                while (t) {
                    new_long.ob_digit[ndig] = t & PyLong_MASK;
                    t >>>= PyLong_SHIFT;
                    ++ndig;
                }
                new_long.ob_size = neg ? -ndig : ndig;
                return new_long;
            } else {
                var ndig, frac, expo, bits;
                var neg = false;

                if (isNaN(v)) {
                    @{{raise}}($new(@{{ValueError}}, B$str("cannot convert float NaN to integer")));
                }
                if (!isFinite(v)) {
                    @{{raise}}($new(@{{OverflowError}}, B$str("cannot convert float infinity to integer")));
                }
                if (v == 0) {
                    new_long.ob_digit[0] = 0;
                    new_long.ob_size = 0;
                    return new_long;
                }
                if (v < 0) {
                    v = -v;
                    neg = true;
                }
                // frac = frexp(dval, &expo); // dval = frac*2**expo; 0.0 <= frac < 1.0
                if (v == 0) {
                    frac = 0;
                    expo = 0;
                } else {
                    expo = Math.log(v)/$log2;
                    expo = (expo < 0 ? Math.ceil(expo):Math.floor(expo)) + 1;
                    frac = v / Math.pow(2.0, expo);
                }
                if (expo <= 0) {
                    return new_long;
                }
                ndig = Math.floor((expo-1) / PyLong_SHIFT) + 1;
                // ldexp(a,b) == a * (2**b)
                frac = frac * Math.pow(2.0, ((expo-1) % PyLong_SHIFT) + 1);
                for (var i = ndig; --i >= 0;) {
                    bits = Math.floor(frac);
                    new_long.ob_digit[i] = bits;
                    frac -= bits;
                    frac = frac * Math.pow(2.0, PyLong_SHIFT);
                }
                new_long.ob_size = neg ? -ndig : ndig;
                return new_long;
            }
            @{{raise}}($new(@{{ValueError}}, B$str("cannot convert " + @{{repr}}(@{{value}}) + "to integer")));
        } else if (typeof v == 'string') {
            var nchars;
            var text = v.replace(/^\s+/, "");
            var i = 0;
            var neg = false;

            switch (text.charAt(0)) {
                case '-':
                    neg = true;
                case '+':
                    text = text.substring(1).replace(/^\s+/, "");
            }

            if (!radix) {
                if (text == '0' || text.charAt(0) != '0') {
                    radix = 10;
                } else {
                    switch (text.charAt(1)) {
                        case 'x':
                        case 'X':
                            radix = 16;
                            break;
                        case 'o':
                        case 'O':
                            radix = 8;
                            break;
                        case 'b':
                        case 'B':
                            radix = 2;
                            break;
                        default:
                            radix = 8;
                            break;
                    }
                }
            } else if (radix < 1 || radix > 36) {
                @{{raise}}($new(@{{ValueError}}, B$str("long() arg 2 must be >= 2 and <= 36")));
            }
            if (text.charAt(0) == '0' && text.length > 1) {
                switch (text.charAt(1)) {
                    case 'x':
                    case 'X':
                        if (radix == 16) text = text.slice(2);
                        break;
                    case 'o':
                    case 'O':
                        if (radix == 8) text = text.slice(2);
                        break;
                    case 'b':
                    case 'B':
                        if (radix == 2) text = text.slice(2);
                        break;

                }
            }
            if ((radix & (radix - 1)) == 0) {
                // binary base: 2, 4, 8, ...
                var n, bits_per_char, accum, bits_in_accum, k, pdigit;
                var p = 0;

                n = radix;
                for (bits_per_char = -1; n; ++bits_per_char) {
                    n >>>= 1;
                }
                n = 0;
                while ($DigitValue[text.charCodeAt(p)] < radix) {
                    p++;
                }
                nchars = p;
                n = p * bits_per_char + PyLong_SHIFT-1; //14 = PyLong_SHIFT - 1
                if (n / bits_per_char < p) {
                    @{{raise}}($new(@{{ValueError}}, B$str("long string too large to convert")));
                }
                new_long.ob_size = n = Math.floor(n/PyLong_SHIFT);
                for (var i = 0; i < n; i++) {
                    new_long.ob_digit[i] = 0;
                }
                // Read string from right, and fill in long from left
                accum = 0;
                bits_in_accum = 0;
                pdigit = 0;
                while (--p >= 0) {
                    k = $DigitValue[text.charCodeAt(p)];
                    accum |= k << bits_in_accum;
                    bits_in_accum += bits_per_char;
                    if (bits_in_accum >= PyLong_SHIFT) {
                        new_long.ob_digit[pdigit] = accum & PyLong_MASK;
                        pdigit++;
                        accum >>>= PyLong_SHIFT;
                        bits_in_accum -= PyLong_SHIFT;
                    }
                }
                if (bits_in_accum) {
                    new_long.ob_digit[pdigit++] = accum;
                }
                while (pdigit < n) {
                    new_long.ob_digit[pdigit++] = 0;
                }
                long_normalize(new_long);
            } else {
                // Non-binary bases (such as radix == 10)
                var c, i, convwidth, convmultmax, convmult, pz, pzstop, scan, size_z;

                if ($log_base_PyLong_BASE[radix] == 0.0) {
                    var i = 1;
                    var convmax = radix;
                    $log_base_PyLong_BASE[radix] = Math.log(radix) / Math.log(PyLong_BASE);
                    while (1) {
                        var next = convmax * radix;
                        if (next > PyLong_BASE) break;
                        convmax = next;
                        ++i;
                    }
                    $convmultmax_base[radix] = convmax;
                    $convwidth_base[radix] = i;
                }
                scan = 0;
                while ($DigitValue[text.charCodeAt(scan)] < radix)
                    ++scan;
                nchars = scan;
                size_z = scan * $log_base_PyLong_BASE[radix] + 1;
                for (var i = 0; i < size_z; i ++) {
                    new_long.ob_digit[i] = 0;
                }
                new_long.ob_size = 0;
                convwidth = $convwidth_base[radix];
                convmultmax = $convmultmax_base[radix];
                for (var str = 0; str < scan;) {
                    c = $DigitValue[text.charCodeAt(str++)];
                    for (i = 1; i < convwidth && str != scan; ++i, ++str) {
                        c = c * radix + $DigitValue[text.charCodeAt(str)];
                    }
                    convmult = convmultmax;
                    if (i != convwidth) {
                        convmult = radix;
                        for ( ; i > 1; --i) convmult *= radix;
                    }
                    pz = 0;
                    pzstop = new_long.ob_size;
                    for (; pz < pzstop; ++pz) {
                        c += new_long.ob_digit[pz] * convmult;
                        new_long.ob_digit[pz] = c & PyLong_MASK;
                        c >>>= PyLong_SHIFT;
                    }
                    if (c) {
                        if (new_long.ob_size < size_z) {
                            new_long.ob_digit[pz] = c;
                            new_long.ob_size++;
                        } else {
                            new_long.ob_digit[new_long.ob_size] = c;
                        }
                    }
                }
            }
            text = text.slice(nchars);
            if (neg) new_long.ob_size = -new_long.ob_size;
            if (text.charAt(0) == 'l' || text.charAt(0) == 'L') text = text.slice(1);
            text = text.replace(/^\s+/, "");
            if (text.length === 0) {
                return new_long;
            }
            @{{raise}}($new(@{{ValueError}}, B$str("invalid literal for long() with base " + @{{!radix}} + ": " + @{{!value}})));
        } else {
            @{{raise}}($new(@{{TypeError}}, B$str("TypeError: long() argument must be a string or a number")));
        }
        if (isNaN(v) || !isFinite(v)) {
            @{{raise}}($new(@{{ValueError}}, B$str("invalid literal for long() with base " + @{{!radix}} + ": '" + @{{!v}} + "'")));
        }
        return new_long;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__init__', ${0, function}$, ['self', 'value', 'radix'], null, null, [${0,None}$], function(self, value, radix) {
        return ${0,None}$;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__str__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str(Format($self, 10, false, false));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__repr__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str(Format($self, 10, true, false));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__nonzero__', ${0, function}$, ['self'], null, null, null, function($self) {
        return $self.ob_size != 0 ? @{{True}} : @{{False}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__cmp__', ${0, function}$, ['self', 'b'], null, null, null, function($self, $b) {
        var sign;

        if ($self.ob_size != $b.ob_size) {
            if ($self.ob_size < $b.ob_size) return -1;
            return 1;
        }
        var i = $self.ob_size < 0 ? - $self.ob_size : $self.ob_size;
        while (--i >= 0 && $self.ob_digit[i] == $b.ob_digit[i])
            ;
        if (i < 0) return 0;
        if ($self.ob_digit[i] < $b.ob_digit[i]) {
            if ($self.ob_size < 0) return 1;
            return -1;
        }
        if ($self.ob_size < 0) return -1;
        return 1;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__hash__', ${0, function}$, ['self'], null, null, null, function($self) {
        var s = @{{long}}['$dict']['__str__']($self);
        var v = $self.valueOf();
        if (v.toString() == s) {
            return "#int#" + v;
        }
        return "#long#" + s;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__invert__', ${0, function}$, ['self'], null, null, null, function($self) {
        var x = @{{long}}['$dict']['__add__']($self, $const_long_1);
        x.ob_size = -x.ob_size;
        return x;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__neg__', ${0, function}$, ['self'], null, null, null, function($self) {
        var x = $new(@{{long}}, 0);
        x.ob_digit = $self.ob_digit.slice(0);
        x.ob_size = -$self.ob_size;
        return x;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__abs__', ${0, function}$, ['self'], null, null, null, function($self) {
        if ($self.ob_size >= 0) return $self;
        var x = $new(@{{long}}, 0);
        x.ob_digit = $self.ob_digit.slice(0);
        x.ob_size = -x.ob_size;
        return x;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__lshift', ${0, jsmethod}$, ['self', 'y'], null, null, null, function($self, $y) {
        var a, z, wordshift, remshift, oldsize, newsize,
            accum, i, j;
        if ($y < 0) {
            @{{raise}}($new(@{{ValueError}}, B$str("negative shift count")));
        }
        if ($y >= $max_float_int) {
            @{{raise}}($new(@{{ValueError}}, B$str("outrageous left shift count")));
        }
        a = $self;

        wordshift = Math.floor($y / PyLong_SHIFT);
        remshift  = $y - wordshift * PyLong_SHIFT;

        oldsize = a.ob_size < 0 ? -a.ob_size : a.ob_size;
        newsize = oldsize + wordshift;
        if (remshift) ++newsize;
        z = $new(@{{long}}, 0);
        z.ob_size = a.ob_size < 0 ? -newsize : newsize;
        for (i = 0; i < wordshift; i++) {
            z.ob_digit[i] = 0;
        }
        accum = 0;
        for (i = wordshift, j = 0; j < oldsize; i++, j++) {
            accum |= a.ob_digit[j] << remshift;
            z.ob_digit[i] = accum & PyLong_MASK;
            accum >>>= PyLong_SHIFT;
        }
        if (remshift) {
            z.ob_digit[newsize-1] = accum;
        }
        z = long_normalize(z);
        return z;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__lshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__lshift']($y.__v);
            case 0x04:
                $y = $y.valueOf();
                return @{{long}}['$dict']['__lshift']($y);
            default: // test for javascript number
                if (typeof $y != "number") break;
                if ($y == Math.floor($y)) return @{{long}}['$dict']['__lshift']($self, $y);
                break;
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rlshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__lshift']($new(@{{long}}, $y.__v), $self.valueOf());
            case 0x04:
                return @{{long}}['$dict']['__lshift']($y, $self.valueOf());
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rshift', ${0, jsmethod}$, ['self', 'y'], null, null, null, function($self, $y) {
        var a, z, size, wordshift, newsize, loshift, hishift,
            lomask, himask, i, j;
        if ($y.__number__ != 0x01) {
            $y = $y.valueOf();
        } else {
            if ($y != Math.floor($y)) {
                @{{raise}}($new(@{{TypeError}}, B$str("unsupported operand type(s) for >>: 'long' and 'float'")));
            }
        }
        if ($y < 0) {
            @{{raise}}($new(@{{ValueError}}, B$str("negative shift count")));
        }
        if ($y >= $max_float_int) {
            @{{raise}}($new(@{{ValueError}}, B$str("shift count too big")));
        }
        a = $self;
        size = $self.ob_size;
        if ($self.ob_size < 0) {
            size = -size;
            a = @{{long}}['$dict']['__add__']($self, $const_long_1);
            a.ob_size = -a.ob_size;
        }

        wordshift = Math.floor($y / PyLong_SHIFT);
        newsize = size - wordshift;
        if (newsize <= 0) {
            z = $const_long_0;
        } else {
            loshift = $y % PyLong_SHIFT;
            hishift = PyLong_SHIFT - loshift;
            lomask = (1 << hishift) - 1;
            himask = PyLong_MASK ^ lomask;
            z = $new(@{{long}}, 0);
            z.ob_size = a.ob_size < 0 ? -newsize : newsize;
            for (i = 0, j = wordshift; i < newsize; i++, j++) {
                z.ob_digit[i] = (a.ob_digit[j] >>> loshift) & lomask;
                if (i+1 < newsize) {
                    z.ob_digit[i] |=
                      (a.ob_digit[j+1] << hishift) & himask;
                }
            }
            z = long_normalize(z);
        }

        if ($self.ob_size < 0) {
            z = @{{long}}['$dict']['__add__'](z, $const_long_1);
            z.ob_size = -z.ob_size;
        }
        return z;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__rshift']($y.__v);
            case 0x04:
                $y = $y.valueOf();
                return @{{long}}['$dict']['__rshift']($y);
            default: // test for javascript number
                if (typeof $y != "number") break;
                if ($y == Math.floor($y)) return @{{long}}['$dict']['__rshift']($self, $y);
                break;
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rrshift__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__rshift']($new(@{{long}}, $y.__v), $self.valueOf());
            case 0x04:
                return @{{long}}['$dict']['__rshift']($y, $self.valueOf());
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__and', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var a, maska, maskb, negz, size_a, size_b, size_z,
            i, z, diga, digb, v, op;

        a = $self;

        if (a.ob_size < 0) {
            a = @{{long}}['$dict']['__invert__'](a);
            maska = PyLong_MASK;
        } else {
            maska = 0;
        }
        if ($b.ob_size < 0) {
            $b = @{{long}}['$dict']['__invert__']($b);
            maskb = PyLong_MASK;
        } else {
            maskb = 0;
        }
        negz = 0;


            op = '&';
            if (maska && maskb) {
                op = '|';
                maska ^= PyLong_MASK;
                maskb ^= PyLong_MASK;
                negz = -1;
            }


        size_a = a.ob_size;
        size_b = $b.ob_size;
        size_z = op == '&'
                    ? (maska
                        ? size_b
                        : (maskb ? size_a : (size_a < size_b ? size_a : size_b)))
                    : (size_a > size_b ? size_a : size_b);
        z = $new(@{{long}}, 0);
        z.ob_size = size_z;

        switch (op) {
            case '&':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga & digb;
                }
                break;
            case '|':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga | digb;
                }
                break;
            case '^':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga ^ digb;
                }
                break;
        }
        z = long_normalize(z);
        if (negz == 0) {
            return z;
        }
        return @{{long}}['$dict']['__invert__'](z);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__and__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__and']($new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__and']($self, $y);
            default: // test for javascript number
                if (typeof $y != "number") break;
                if ($y == Math.floor($y)) return @{{long}}['$dict']['__and']($self, $y);
                break;
        }
        return @{{NotImplemented}};
    });

    ${0,funcbase}$['__rand__'] = ${0,funcbase}$['__and__'];

    func(${0,module}$, null, ${0,funcbase}$, '__xor', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var a,maska, maskb, negz, size_a, size_b, size_z,
            i, z, diga, digb, v, op;

        a = $self;

        if (a.ob_size < 0) {
            a = @{{long}}['$dict']['__invert__'](a);
            maska = PyLong_MASK;
        } else {
            maska = 0;
        }
        if ($b.ob_size < 0) {
            $b = @{{long}}['$dict']['__invert__']($b);
            maskb = PyLong_MASK;
        } else {
            maskb = 0;
        }
        negz = 0;


            op = '^';
            if (maska != maskb) {
                maska ^= PyLong_MASK;
                negz = -1;
            }


        size_a = a.ob_size;
        size_b = $b.ob_size;
        size_z = op == '&'
                    ? (maska
                        ? size_b
                        : (maskb ? size_a : (size_a < size_b ? size_a : size_b)))
                    : (size_a > size_b ? size_a : size_b);
        z = $new(@{{long}}, 0);
        z.ob_size = size_z;

        switch (op) {
            case '&':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga & digb;
                }
                break;
            case '|':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga | digb;
                }
                break;
            case '^':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga ^ digb;
                }
                break;
        }
        z = long_normalize(z);
        if (negz == 0) {
            return z;
        }
        return @{{long}}['$dict']['__invert__'](z);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__xor__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__xor']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__xor']($self, $y);
            default: // test for javascript number
                if (typeof $y != "number") break;
                if ($y == Math.floor($y)) return @{{long}}['$dict']['__xor']($self, $y);
                break;
        }
        return @{{NotImplemented}};
    });

    ${0,funcbase}$['__rxor__'] = ${0,funcbase}$['__xor__'];

    func(${0,module}$, null, ${0,funcbase}$, '__or', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var a, maska, maskb, negz, size_a, size_b, size_z,
            i, z, diga, digb, v, op;

        a = $self;

        if (a.ob_size < 0) {
            a = @{{long}}['$dict']['__invert__'](a);
            maska = PyLong_MASK;
        } else {
            maska = 0;
        }
        if ($b.ob_size < 0) {
            $b = @{{long}}['$dict']['__invert__']($b);
            maskb = PyLong_MASK;
        } else {
            maskb = 0;
        }
        negz = 0;


            op = '|';
            if (maska || maskb) {
                op = '&';
                maska ^= PyLong_MASK;
                maskb ^= PyLong_MASK;
                negz = -1;
            }


        size_a = a.ob_size;
        size_b = $b.ob_size;
        size_z = op == '&'
                    ? (maska
                        ? size_b
                        : (maskb ? size_a : (size_a < size_b ? size_a : size_b)))
                    : (size_a > size_b ? size_a : size_b);
        z = $new(@{{long}}, 0);
        z.ob_size = size_z;

        switch (op) {
            case '&':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga & digb;
                }
                break;
            case '|':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga | digb;
                }
                break;
            case '^':
                for (i = 0; i < size_z; ++i) {
                    diga = (i < size_a ? a.ob_digit[i] : 0) ^ maska;
                    digb = (i < size_b ? $b.ob_digit[i] : 0) ^ maskb;
                    z.ob_digit[i] = diga ^ digb;
                }
                break;
        }
        z = long_normalize(z);
        if (negz == 0) {
            return z;
        }
        return @{{long}}['$dict']['__invert__'](z);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__or__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__or']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__or']($self, $y);
            default: // test for javascript number
                if (typeof $y != "number") break;
                if ($y == Math.floor($y)) return @{{long}}['$dict']['__or']($self, $y);
                break;
        }
        return @{{NotImplemented}};
    });

    ${0,funcbase}$['__ror__'] = ${0,funcbase}$['__or__'];

    func(${0,module}$, null, ${0,funcbase}$, '__oct__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str(Format($self, 8, true, false));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__hex__', ${0, function}$, ['self'], null, null, null, function($self) {
        return B$str(Format($self, 16, true, false));
    });

    func(${0,module}$, null, ${0,funcbase}$, '__add', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var a = $self, z;
        if (a.ob_size < 0) {
            if ($b.ob_size < 0) {
                z = x_add(a, $b);
                z.ob_size = -(z.ob_size);
            }
            else {
                z = x_sub($b, a);
            }
        }
        else {
            z = $b.ob_size < 0 ? x_sub(a, $b) : x_add(a, $b);
        }
        return z;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__add__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__add']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__add']($self, $y);
        }
        return @{{NotImplemented}};
    });

    ${0,funcbase}$['__radd__'] = ${0,funcbase}$['__add__'];

    func(${0,module}$, null, ${0,funcbase}$, '__sub', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var a = $self, z;
        if (a.ob_size < 0) {
            z = $b.ob_size < 0 ? x_sub(a, $b) : x_add(a, $b);
            z.ob_size = -(z.ob_size);
        }
        else {
            z = $b.ob_size < 0 ?  x_add(a, $b) : x_sub(a, $b);
        }
        return z;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__sub__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__sub']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__sub']($self, $y);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rsub__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__sub']($new(@{{long}}, $y.__v), $self);
            case 0x04:
                return @{{long}}['$dict']['__sub']($y, $self);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__mul', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        //var z = k_mul($self, $b);
        var z = x_mul($self, $b);
        if (($self.ob_size ^ $b.ob_size) < 0)
            z.ob_size = -(z.ob_size);
        return z;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__mul__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__mul']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__mul']($self, $y);
        }
        return @{{NotImplemented}};
    });

    ${0,funcbase}$['__rmul__'] = ${0,funcbase}$['__mul__'];

    func(${0,module}$, null, ${0,funcbase}$, '__div', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var div = $new(@{{long}}, 0);
        l_divmod($self, $b, div, null);
        return div;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__div__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__div']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__div']($self, $y);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rdiv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__div']($new(@{{long}}, y.__v), $self);
            case 0x04:
                return @{{long}}['$dict']['__div']($y, $self);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__mod', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var mod = $new(@{{long}}, 0);
        l_divmod($self, $b, null, mod);
        return mod;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__mod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__mod']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__mod']($self, $y);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rmod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__mod']($new(@{{long}}, $y.__v), $self);
            case 0x04:
                return @{{long}}['$dict']['__mod']($y, $self);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__divmod', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var div = $new(@{{long}}, 0);
        var mod = $new(@{{long}}, 0);
        l_divmod($self, $b, div, mod);
        return @{{tuple}}([div, mod]);
    });

    func(${0,module}$, null, ${0,funcbase}$, '__divmod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__divmod']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__divmod']($self, $y);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rdivmod__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__divmod']($new(@{{long}}, y.__v), $self);
            case 0x04:
                return @{{long}}['$dict']['__divmod']($y, $self);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__floordiv', ${0, jsmethod}$, ['self', 'b'], null, null, null, function($self, $b) {
        var div = $new(@{{long}}, 0);
        l_divmod($self, $b, div, null);
        return div;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__floordiv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__floordiv']($self, $new(@{{long}}, $y.__v));
            case 0x04:
                return @{{long}}['$dict']['__floordiv']($self, $y);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__rfloordiv__', ${0, function}$, ['self', 'y'], null, null, null, function($self, $y) {
        switch ($y.__number__) {
            case 0x02:
                return @{{long}}['$dict']['__floordiv']($new(@{{long}}, y.__v), $self);
            case 0x04:
                return @{{long}}['$dict']['__floordiv']($y, $self);
        }
        return @{{NotImplemented}};
    });

    func(${0,module}$, null, ${0,funcbase}$, '__pow', ${0, jsmethod}$, ['self', 'w', 'x'], null, null, null, function($self, $w, $x) {
        var v = $self;
        var a, b, c, negativeOutput = 0, z, i, j, k, temp, bi;
        var table = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];

        a = $self;
        b = $w.__number__ == 0x04 ? $w : $new(@{{long}}, $w);
        if ($x === null || typeof $x == "undefined") {
            c = null;
        } else {
            c = $x.__number__ == 0x04 ? $x : $new(@{{long}}, $x);
        }

        if (b.ob_size < 0) {
            if (c !== null) {
                @{{raise}}($new(@{{TypeError}}, B$str("pow() 2nd argument cannot be negative when 3rd argument specified")));
            }
            return Math.pow(v.valueOf(), $w.valueOf());
        }

        if (c !== null) {
            if (c.ob_size == 0) {
                @{{raise}}($new(@{{ValueError}}, B$str("pow() 3rd argument cannot be 0")));
            }
            if (c.ob_size < 0) {
                negativeOutput = 1;
                temp = $pow_temp_c;
                temp.ob_digit = c.ob_digit.slice(0);
                temp.ob_size = -c.ob_size;
                c = temp;
            }
            if (c.ob_size == 1 && c.ob_digit[0] == 1) {
                return $const_long_0;
            }
            if (a.ob_size < 0) {
                temp = $pow_temp_a;
                l_divmod(a, c, null, temp);
                a = temp;
            }
        }
        z = $new(@{{long}}, 1);
        temp = $pow_temp_z;
        if (b.ob_size <= FIVEARY_CUTOFF) {
            for (i = b.ob_size - 1; i >= 0; --i) {
                bi = b.ob_digit[i];
                for (j = 1 << (PyLong_SHIFT-1); j != 0; j >>>= 1) {
                    z = @{{long}}['$dict']['__mul'](z, z);
                    if (c !== null) {
                        l_divmod(z, c, null, temp);
                        z.ob_digit = temp.ob_digit.slice(0);
                        z.ob_size = temp.ob_size;
                    }
                    if (bi & j) {
                        z = @{{long}}['$dict']['__mul'](z, a);
                        if (c !== null) {
                            l_divmod(z, c, null, temp);
                            z.ob_digit = temp.ob_digit.slice(0);
                            z.ob_size = temp.ob_size;
                        }
                    }
                }
            }
        } else {
            table[0] = z;
            for (i = 1; i < 32; ++i) {
                table[i] = @{{long}}['$dict']['__mul'](table[i-1], a);
                if (c !== null) {
                    l_divmod(table[i], c, null, temp);
                    table[i].ob_digit = temp.ob_digit.slice(0);
                    table[i].ob_size = temp.ob_size;
                }
            }
            for (i = b.ob_size - 1; i >= 0; --i) {
                bi = b.ob_digit[i];
                for (j = PyLong_SHIFT - 5; j >= 0; j -= 5) {
                    var index = (bi >>> j) & 0x1f;
                    for (k = 0; k < 5; ++k) {
                        z = @{{long}}['$dict']['__mul'](z, z);
                        if (c !== null) {
                            l_divmod(z, c, null, temp);
                            z.ob_digit = temp.ob_digit.slice(0);
                            z.ob_size = temp.ob_size;
                        }
                    }
                    if (index) {
                        z = @{{long}}['$dict']['__mul'](z, table[index]);
                        if (c !== null) {
                            l_divmod(z, c, null, temp);
                            z.ob_digit = temp.ob_digit.slice(0);
                            z.ob_size = temp.ob_size;
                        }
                    }
                }
            }
        }

        if ((c !== null) && negativeOutput &&
            (z.ob_size != 0) && (c.ob_size != 0)) {
            z = @{{long}}['$dict']['__sub__'](z, c);
        }
        return z;
    });

    func(${0,module}$, null, ${0,funcbase}$, '__pow__', ${0, function}$, ['self', 'y', 'z'], null, null, null, function($self, $y, $z) {
        switch ($y.__number__) {
            case 0x02:
                if (typeof $z == "undefined")
                    return @{{long}}['$dict']['__pow']($self, $new(@{{long}}, $y.__v), null);
                switch ($z.__number) {
                    case 0x02:
                        return @{{long}}['$dict']['__pow']($self, $new(@{{long}}, $y.__v), $new(@{{long}}, $z));
                    case 0x04:
                        return @{{long}}['$dict']['__pow']($self, $new(@{{long}}, $y.__v), $z);
                }
                break;
            case 0x04:
                if (typeof $z == "undefined")
                    return @{{long}}['$dict']['__pow']($self, $y, null);
                switch (z.__number) {
                    case 0x02:
                        return @{{long}}['$dict']['__pow']($self, $y, $new(@{{long}}, $z));
                    case 0x04:
                        return @{{long}}['$dict']['__pow']($self, $y, $z);
                }
                break;
        }
        return @{{NotImplemented}};
    });


    var $const_long_0 = $new(@{{long}}, 0),
        $const_long_1 = $new(@{{long}}, 1);
    // Since javascript is single threaded:
    var $l_divmod_div = $new(@{{long}}, 0),
        $l_divmod_mod = $new(@{{long}}, 0),
        $x_divrem_v = $new(@{{long}}, 0),
        $x_divrem_w = $new(@{{long}}, 0),
        $pow_temp_a = $new(@{{long}}, 0),
        $pow_temp_c = $new(@{{long}}, 0),
        $pow_temp_z = $new(@{{long}}, 0);
})();""")


# Now we're done with the str/int/bool/long/NonType classes and
# can initialize the constants
JS("""
init_short_names$(true);
init_constants$(true);
@{{__name__}} = B$str('__builtin__');
@{{__builtins__}} = ${0,module}$;


func(${0,module}$, null, ${0,funcbase}$, 'op_not', ${0, function}$, ['object'], null, null, null, function($object) {
${1, new_bool, $object, @{{False}}, @{{True}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'test_not', ${0, function}$, ['object'], null, null, null, function($object) {
${1, new_bool, $object, false, true}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_neg', ${0, function}$, ['v'], null, null, null, function($v) {
    if (typeof $v != "undefined" && $v !== null) {
        var f, mro$;
${2, getattribute, mro$, f, $v, '__neg__'}$
        if (typeof f == 'function') return f($v);
    }
    return @{{raise}}($new(@{{TypeError}}, B$str(@{{sprintf}}("bad operand type for unary -: '%r'", [$v]))));
});

func(${0,module}$, null, ${0,funcbase}$, 'op_inv', ${0, function}$, ['v'], null, null, null, function($v) {
    if (typeof $v != "undefined" && $v !== null) {
        var f, mro$;
${2, getattribute, mro$, f, $v, '__invert__'}$
        if (typeof f == 'function') return f($v);
    }
    return @{{raise}}($new(@{{TypeError}}, B$str(@{{sprintf}}("bad operand type for unary ~: '%r'", [$v]))));
});


func(${0,module}$, null, ${0,funcbase}$, 'op_is', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, is, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_is_not', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, is_not, $a, $b, @{{False}}, @{{True}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_eq', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, ==, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_ne', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, !=, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_le', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, <=, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_lt', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, <, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_ge', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, >=, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_gt', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, >, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_in', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, in, $a, $b, @{{True}}, @{{False}}}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_not_in', ${0, function}$, ['a', 'b'], null, null, null, function($a, $b) {
${1, op_compare, not_in, $a, $b, @{{False}}, @{{True}}}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_add', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
    if (typeof $x == 'string' && typeof $y == 'string') {
        return $new(@{{str}}, $x + $y);
    }
${1, op_arithmetic, +, add, $x, $y, $i}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_sub', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
${1, op_arithmetic, -, sub, $x, $y, $i}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_floordiv', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
${1, op_arithmetic, //, floordiv, $x, $y, $i, Math.floor}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_div', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
${1, op_arithmetic, /, div, $x, $y, $i}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_truediv', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
${1, op_arithmetic, /, truediv, $x, $y, $i}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_mul', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
${1, op_arithmetic, *, mul, $x, $y, $i}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_mod', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
    if ($x.__class__ == @{{str}}) {
        return @{{sprintf}}($x, $y);
    }
${1, op_arithmetic, %, mod, $x, $y, $i, mod}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_pow', ${0, function}$, ['x', 'y', 'i'], null, null, null, function($x, $y, $i) {
${1, op_arithmetic, **, pow, $x, $y, $i, Math.pow}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_bitand2', ${0, function}$, ['x', 'y'], null, null, null, function($x, $y) {
${1, op_bitexpr2, &, and, $x, $y}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_bitand', ${0, function}$, ['args'], null, null, null, function($args) {
${1, op_bitexpr, &, and, $args}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_bitor2', ${0, function}$, ['x', 'y'], null, null, null, function($x, $y) {
${1, op_bitexpr2, |, or, $x, $y}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_bitor', ${0, function}$, ['args'], null, null, null, function($args) {
${1, op_bitexpr, |, or, $args}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_bitxor2', ${0, function}$, ['x', 'y'], null, null, null, function($x, $y) {
${1, op_bitexpr2, ^, xor, $x, $y}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_bitxor', ${0, function}$, ['args'], null, null, null, function($args) {
${1, op_bitexpr, ^, xor, $args}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_bitlshift2', ${0, function}$, ['x', 'y'], null, null, null, function($x, $y) {
${1, op_bitexpr2, <<, lshift, $x, $y}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_bitlshift', ${0, function}$, ['args'], null, null, null, function($args) {
${1, op_bitexpr, <<, lshift, $args}$
});

func(${0,module}$, null, ${0,funcbase}$, 'op_bitrshift2', ${0, function}$, ['x', 'y'], null, null, null, function($x, $y) {
${1, op_bitexpr2, <<, rshift, $x, $y}$
});
func(${0,module}$, null, ${0,funcbase}$, 'op_bitrshift', ${0, function}$, ['args'], null, null, null, function($args) {
${1, op_bitexpr, <<, rshift, $args}$
});


// Some internal functions

function tplass(t, n) {
    if (t.__array.length != n) {
        if (t.__array.length > n) {
            @{{raise}}($new(@{{ValueError}}, B$str("too many values to unpack")));
        } else {
            @{{raise}}($new(@{{ValueError}}, B$str("need more than " + n + " values to unpack")));
        }
    }
    return t;
}
@{{tplass}} = tplass;

function _isJsInt(v) {
    if (typeof v == 'number') {
        if (v > 0) {
            return v == Math.floor(v);
        } else {
            return v == Math.ceil(v);
        }
    }
    return false;
}

function _typeof(obj) {
    if (typeof obj['__class__'] != 'undefined') {
        return obj['__class__']['__name__'].valueOf();
    }
    return typeof obj;
}

""")



########################################################################################



def __dynamic_load__(importName):
    global __nondynamic_modules__
    setCompilerOptions("noDebug")
    module = JS("""$pyjs.loaded_modules[@{{importName}}]""")
    if sys is None or dynamic is None or __nondynamic_modules__.has_key(importName):
        return module
    if JS("""typeof @{{module}} == "undefined" """):
        try:
            dynamic.ajax_import("lib/" + importName + ".__" + platform + "__.js")
            module = JS("""$pyjs.loaded_modules[@{{importName}}]""")
        except:
            pass
    if JS("""typeof @{{module}} == "undefined" """):
        try:
            dynamic.ajax_import("lib/" + importName + ".js")
            module = JS("""$pyjs.loaded_modules[@{{importName}}]""")
        except:
            pass
        if JS("""typeof @{{module}} == "undefined" """):
            __nondynamic_modules__[importName] = 1.0
    return module

class BaseException:

    def __init__(self, *args):
        self.args = args
        #JS("@{{self}}.toString = function() {return @{{mcall}}(null, null, this, '__str__');}")

    def __getitem__(self, index):
        return self.args[index]

    def __str__(self):
        args = self.args
        if len(args) is 0:
            return ''
        elif len(args) is 1:
            return str(args[0])
        return repr(args)

    def __repr__(self):
        if callable(self):
            return "<type '%s'>" % self.__name__
        return self.__name__ + repr(self.args)

class KeyboardInterrupt(BaseException):
    pass

class Exception(BaseException):
    pass

class StandardError(Exception):
    pass

class AssertionError(StandardError):
    pass

class GeneratorExit(Exception):
    pass

class TypeError(StandardError):
    pass

class AttributeError(StandardError):
    pass

class NameError(StandardError):
    pass

class ValueError(StandardError):
    pass

class ImportError(StandardError):
    pass

class LookupError(StandardError):
    pass

class RuntimeError(StandardError):
    pass

class ArithmeticError(StandardError):
    pass

class SystemError(StandardError):
    pass

class KeyError(LookupError):

    def __str__(self):
        if len(self.args) is 0:
            return ''
        elif len(self.args) is 1:
            return repr(self.args[0])
        return repr(self.args)

class IndexError(LookupError):
    pass

class NotImplementedError(RuntimeError):
    pass

class ZeroDivisionError(ArithmeticError):
    pass

class OverflowError(ArithmeticError):
    pass

class UndefinedValueError(ValueError):
    pass

class StopIteration(Exception):
    pass

# StopIteration is used to get out of an iteration loop
StopIter = StopIteration()

class TryElseException(Exception):
    pass

# The TryElse 'error' is used to implement the else in try-except-else
# (to raise an exception when there wasn't any)
TryElse = TryElseException()

empty_str = ""
empty_tuple = ()

def init():

    # There seems to be an bug in Chrome with accessing the message
    # property, on which an error is thrown
    # Hence the declaration of 'var message' and the wrapping in try..catch
    JS("""
@{{_errorMapping}} = function(err) {
    if (err instanceof(ReferenceError) || err instanceof(TypeError)) {
        var message = '';
        try {
            message = err.message;
        } catch ( e) {
        }
        return @{{AttributeError}}(message);
    }
    return err;
};
""")

def open(fname, mode='r'):
    raise NotImplementedError("open is not implemented in browsers")

def abs(a):
    if a < 0:
        return -a
    return a

def cmp(a, b):
    JS("""
    var a = @{{a}}, b = @{{b}};
    var av = a.valueOf(),
        bv = b.valueOf(),
        amro, bmro, f;
    if (typeof av == typeof bv) {
        switch (typeof av) {
            case 'number':
            case 'string':
            case 'boolean':
                return av == bv ? @{{int_zero}} : (av < bv ? @{{int_minus_one}} : @{{int_one}});
        }
    }
    if (av === null) {
        if (bv === null) return @{{int_zero}};
        return @{{int_minus_one}};
    }
    if (bv === null) {
        return @{{int_one}};
    }
    if (a['$inst'] === true) {
        if (b['$inst'] == true) {
            switch ((a.__number__ << 8)|b.__number__) {
                case 0x0101:
                case 0x0102:
                case 0x0201:
                case 0x0202:
                    // a and b are int and/or float
                    return a == b ? @{{int_zero}} : (a < b ? @{{int_minus_one}} : @{{int_one}});

                case 0x0100:
                case 0x0200:
                case 0x0400:
                    f = @{{_getattr}}($b, '__cmp__');
                    if (typeof f != "undefined") {
                        return @{{op_neg}}(@{{fcall}}(this, null, f, $b, $a));
                    }
                    return @{{int_minus_one}};
                case 0x0001:
                case 0x0002:
                case 0x0004:
                    f = @{{_getattr}}($a, '__cmp__');
                    if (typeof f != "undefined") {
                        return @{{fcall}}(this, null, f, $a, $b);
                    }
                    return @{{int_one}};
                case 0x0104:
                case 0x0204:
                    return @{{mcall}}(this, null, $new(@{{long}}, av), '__cmp__', b);
                case 0x0401:
                case 0x0402:
                    return @{{mcall}}(this, null, a, '__cmp__', $new(@{{long}}, bv));
                case 0x0404:
                    return @{{mcall}}(this, null, a, '__cmp__', b);
            }
            try {
                return @{{mcall}}(this, null, a, '__cmp__', b);
            } catch (e) {
            }
            if ($a === $b) return @{{int_zero}};
            if (a === b) return @{{int_zero}};
            if (typeof $a[$hash_id_name$] != 'undefined') {
                if (typeof $b[$hash_id_name$] != 'undefined') {
                    if ($a[$hash_id_name$] < $b[$hash_id_name$]) {
                        return @{{int_minus_one}};
                    }
                    if ($a[$hash_id_name$] > $b[$hash_id_name$]) {
                        return @{{int_one}};
                    }
                    return @{{int_zero}};
                }
                return @{{int_minus_one}};
            }
            if (typeof $b[$hash_id_name$] != 'undefined') {
                return @{{int_one}};
            }
            // Just do something consistent
            return a.__class__.__name__ < b.__class__.__name__ ? @{{int_minus_one}} : @{{int_one}};
        }
        return @{{int_minus_one}};
    } else if (b['$inst'] === true) {
        return @{{int_minus_one}};
    } else if (a['$inst'] === false && b['$inst'] === false) {
        if (a === b) return @{{int_zero}};
        // Just do something consistent
        return a.__class__.__name__ < b.__class__.__name__ ? @{{int_minus_one}} : @{{int_one}};
    }
    if (a == b) return @{{int_zero}};
    if (a > b) return @{{int_one}};
    return @{{int_minus_one}};
    """)


class NotImplementedType(object):
    def __repr__(self):
        return "<type 'NotImplementedType'>"
    def __str__(self):
        self.__repr__()
    def toString(self):
        self.__repr__()
NotImplemented = NotImplementedType()

JS("""
var $iter_array = function (l) {
    this.__array = l;
    this.i = -1;
};
$iter_array.prototype.next = function (noStop) {
    if (++this.i == this.__array.length) {
        if (noStop === true) {
            return;
        }
        throw @{{StopIteration}};
    }
    return this.__array[this.i];
};
$iter_array.prototype.__iter__ = function ( ) {
    return this;
};
var $reversed_iter_array = function (l) {
    this.___array = l;
    this.i = l.length;
};
$reversed_iter_array.prototype.next = function (noStop) {
    if (--this.i == -1) {
        if (noStop === true) {
            return;
        }
        throw @{{StopIteration}};
    }
    return this.___array[this.i];
};
$reversed_iter_array.prototype.__iter__ = function ( ) {
    return this;
};
//$reversed_iter_array.prototype.$genfunc = $reversed_iter_array.prototype.next;
var $enumerate_array = function (l) {
    this.array = l;
    this.i = -1;
    this.tuple = $new(@{{tuple}}, [@{{int_zero}}, B$str("")]);
    this.tuple = @{{tuple}}.__call__(foo);
    this.tuple = @{{tuple}}.__call__([@{{int}}.__call__(0), B$str("")]);
    this.tl = this.tuple.__array;
};
$enumerate_array.prototype.next = function (noStop, reuseTuple) {
    if (++this.i == this.array.length) {
        if (noStop === true) {
            return;
        }
        throw @{{StopIteration}};
    }
    this.tl[1] = this.array[this.i];
    if (this.tl[0].__number__ == 0x01) {
        this.tl[0] = this.i;
    } else {
        this.tl[0] = new @{{int}}(this.i);
    }
    return reuseTuple === true ? this.tuple : @{{tuple}}(this.tl);
};
$enumerate_array.prototype.__iter__ = function ( ) {
    return this;
};
$enumerate_array.prototype.$genfunc = $enumerate_array.prototype.next;
""")
# NOTE: $genfunc is defined to enable faster loop code


class slice(object):

    def __init__(self, start, *args):
        if len(args) == 0:
            self.start = None
            self.stop = start
        else:
            self.start = start
            self.stop = args[0]
        if len(args) == 2:
            self.step = args[1]
        else:
            self.step = None

    def indices(self, n):
        step = 1 if self.step is None else self.step
        if step > 0:
            start = 0 if self.start is None else self.start
            stop = n if self.stop is None else self.stop
            if start > n:
                start = n
            elif start < 0:
                start = 0
            if stop < 0:
                stop += n
                if stop < start:
                    stop = start
            elif stop > n:
                stop = n
        else:
            start = n - 1 if self.start is None else self.start
            stop = -1 if self.stop is None else self.stop
            if stop > 0:
                if stop > start:
                    stop += n
                    if stop > start:
                        stop = start
            else:
                if start > n:
                    start = n - 1
                if stop <= -n:
                    stop += n
                    if stop < -1:
                        stop = -1
        return (start, stop, step)

    def __str__(self):
        return "slice(%s, %s, %s)" % (self.start, self.stop, self.step)

    def __repr__(self):
        return "slice(%s, %s, %s)" % (self.start, self.stop, self.step)

    def __hash__(self):
        raise TypeError("unhashable type")

    def __cmp__(self, y):
        a_start = 0 if self.start is None else self.start
        a_step = 1 if self.step is None else self.step
        b_start = 0 if y.start is None else y.start
        b_step = 1 if y.step is None else y.step
        if a_start < b_start:
            return -1
        if a_start > b_start:
            return 1
        if self.stop is not y.stop:
            if self.stop is None:
                return 1
            if y.stop is None:
                return -1
            if self.stop < y.stop:
                return -1
            return 1
        if a_step < b_step:
            return -1
        if a_step > b_step:
            return 1
        return 0

    # finally add builtin __slots__ for slice
    JS("""${0,funcbase}$['__slots__'] = ['start', 'stop', 'step'];""")


class list(object):

    __new__ = JS("""function(cls, value) {
${2, __new__, new_list, cls}$
        new_list['__array'] = [];
        new_list['valueOf'] = function() {return this.__array;};
        new_list['toString'] = function() {return this['__class__']['$dict']['__str__'](this).valueOf();};
        return new_list;
    }""")

    __init__ = tuple.__init__


    def __hash__(self):
        raise TypeError("list objects are unhashable")

    def append(self, item):
        JS("""@{{self}}.__array[@{{self}}.__array.length] = @{{item}};""")

    # extend in place, just in case there's somewhere a shortcut to self.__array
    def extend(self, data):
        # Transform data into an array and append to self.__array
        JS("""
        if (@{{data}} === null) {
            @{{raise}}($new(@{{TypeError}}, B$str("'NoneType' is not iterable")));
        }
        if (@{{data}} instanceof Array) {
        } else if (@{{data}}.__array instanceof Array) {
            @{{data}} = @{{data}}.__array;
        } else if (typeof @{{data}}.__iter__ == 'function') {
            if (typeof @{{data}}.__array == 'object') {
                @{{data}} = @{{data}}.__array;
            } else {
                var iter = @{{data}}.__iter__();
                if (typeof iter.__array == 'object') {
                    @{{data}} = iter.__array;
                }
                @{{data}} = [];
                var item, i = 0;
                if (typeof iter.$genfunc == 'function') {
                    while (typeof (item=iter.next(true)) != "undefined") {
                        @{{data}}[i++] = item;
                    }
                } else {
                    try {
                        while (true) {
                            @{{data}}[i++] = iter.next();
                        }
                    }
                    catch (e) {
                        if (e.__name__ != 'StopIteration') throw e;
                    }
                }
            }
        } else {
            @{{raise}}($new(@{{TypeError}}, B$str("'" + @{{repr}}(@{{data}}) + "' is not iterable")));
        }
        var l = @{{self}}.__array;
        var j = @{{self}}.__array.length;
        var n = @{{data}}.length, i = 0;
        while (i < n) {
            l[j++] = @{{data}}[i++];
        }
        """)

    def remove(self, value):
        index = self.index(value)
        JS("""
        var index = @{{index}}.valueOf();
        if (index<0) {
            @{{raise}}($new(@{{ValueError}}, B$str("list.remove(x): x not in list")));
        }
        @{{self}}.__array.splice(index, 1);
        return true;
        """)

    count = tuple.count

    def index(self, value, _start=0):
        JS("""
        var start = @{{_start}}.valueOf();
        /* if (typeof valueXXX == 'number' || typeof valueXXX == 'string') {
            start = selfXXX.__array.indexOf(valueXXX, start);
            if (start >= 0)
                return start;
        } else */ {
            var len = @{{self}}.__array.length >>> 0;

            start = (start < 0)
                    ? Math.ceil(start)
                    : Math.floor(start);
            if (start < 0)
                start += len;

            for (; start < len; start++) {
                if ( /*start in selfXXX.__array && */
                    @{{cmp}}(@{{self}}.__array[start], @{{value}}) === @{{int_zero}})
                    return $new(@{{int}}, start);
            }
        }
        """)
        raise ValueError("list.index(x): x not in list")

    def insert(self, index, value):
        JS("""    var a = @{{self}}.__array; @{{self}}.__array=a.slice(0, @{{index}}).concat(@{{value}}, a.slice(@{{index}}));""")

    def pop(self, _index = -1):
        JS("""
        var index = @{{_index}}.valueOf();
        if (index<0) index += @{{self}}.__array.length;
        if (index < 0 || index >= @{{self}}.__array.length) {
            if (@{{self}}.__array.length == 0) {
                @{{raise}}($new(@{{IndexError}}, B$str("pop from empty list")));
            }
            @{{raise}}($new(@{{IndexError}}, B$str("pop index out of range")));
        }
        var a = @{{self}}.__array[index];
        @{{self}}.__array.splice(index, 1);
        return a;
        """)

    def __cmp__(self, l):
        if not isinstance(l, list):
            return -1
        JS("""
        var n1 = @{{self}}.__array.length,
            n2 = @{{l}}.__array.length,
            a1 = @{{self}}.__array,
            a2 = @{{l}}.__array,
            n, c;
        n = (n1 < n2 ? n1 : n2);
        for (var i = 0; i < n; i++) {
            c = @{{cmp}}(a1[i], a2[i]);
            if (c !== @{{int_zero}}) return c;
        }
        if (n1 < n2) return @{{int_minus_one}};
        if (n1 > n2) return @{{int_one}};
        return @{{int_zero}};""")

    def __getslice__(self, lower, upper):
        JS("""
        var lower = @{{lower}}.valueOf() || 0,
            upper = @{{upper}}.valueOf() === null ? @{{self}}.__array.length : @{{upper}}.valueOf();
        return @{{list}}(@{{self}}.__array.slice(lower, upper));
        """)

    def __delslice__(self, lower, upper):
        JS("""
        var lower = @{{lower}}.valueOf() || 0,
            upper = @{{upper}}.valueOf() === null ? @{{self}}.__array.length : @{{upper}}.valueOf(),
            n = upper - lower;
        if (n > 0) @{{self}}.__array.splice(lower, n);
        """)
        return None

    def __setslice__(self, lower, upper, data):
        self.__delslice__(lower, upper)
        tail = self.__getslice__(lower, None)
        self.__delslice__(lower, None)
        self.extend(data)
        self.extend(tail)
        return None

    __getitem__ = tuple.__getitem__

    def __setitem__(self, _index, value):
        JS("""
        if (@{{isinstance}}(@{{_index}}, [@{{int}}, @{{long}}]) === ${0,True}$) {
            var index = @{{_index}}.valueOf();
            if (index < 0) index += @{{self}}.__array.length;
            if (index < 0 || index >= @{{self}}.__array.length) {
                @{{raise}}($new(@{{IndexError}}, B$str("list assignment index out of range")));
            }
            @{{self}}.__array[index]=@{{value}};
            return ${0,None}$
        }
        if (@{{isinstance}}(@{{_index}}, [@{{slice}}]) === ${0, True}$) {
            var start = @{{_index}}['$dict']['start'].valueOf(),
                stop = @{{_index}}['$dict']['stop'].valueOf(),
                step = @{{_index}}['$dict']['step'].valueOf(),
                __array = [],
                _stopiter = @{{stopiter}},
                iter, next, i, k, v, n_todo, n_done = 0;
            if (start === null) start = 0;
            if (stop === null || stop > $self.__array.length) stop = $self.__array.length;
            if (step === null) step = 1;
            if (step == 1) {
                return @{{self}}['__class__']['$dict']['__setslice__'](@{{self}}, $new(@{{int}}, start), $new(@{{int}}, stop), @{{value}});
            }
            debugger; // rest not tested
            n_todo = Math.floor((stop - start)/step);
            iter = @{{fcall}}(this, null, @{{iter}}, null, @{{value}});
            next = @{{getattr}}(iter, 'next');
            for (;;) {
                @{{stopiter}} = true;
                v = @{{fcall}}(this, null, next, iter);
                @{{stopiter}} = _stopiter;
                if (v === @{{StopIter}}) {
                    break;
                }
                __array.append(v);
            }
            if (n_todo != __array.length) {
                return @{{raise}}($new(@{{ValueError}}, B$sttr("attempt to assign sequence of size " + __array.length + " to extended slice of size " + n_todo)));
            }
            for (i = 0; start < stop; start += step, i++) {
                @{{self}}.__array[start] = __array[i];
            }
            return B$tuple(__array);
        }
        @{{raise}}($new(@{{TypeError}}, B$str(_typeof(@{{self}}) + " indices must be integers, not " + _typeof(@{{index}}))));
        """)

    def __delitem__(self, _index):
        JS("""
        var index = @{{_index}}.valueOf();
        if (index < 0) index += @{{self}}.__array.length;
        if (index < 0 || index >= @{{self}}.__array.length) {
            @{{raise}}($new(@{{IndexError}}, B$str("list assignment index out of range")));
        }
        @{{self}}.__array.splice(index, 1);
        """)

    def __len__(self):
        return JS("""$new(@{{int}}, @{{self}}.__array.length)""")

    def __contains__(self, value):
        try:
            self.index(value)
        except ValueError:
            return False
        return True

    def __iter__(self):
        return listiterator(self)

    def __reversed__(self):
        return listreverseiterator(self)

    def __enumerate__(self):
        return JS("new $enumerate_array(@{{self}}.__array)")

    def reverse(self):
        JS("""@{{self}}.__array.reverse();""")

    def sort(self, cmp_=None, key=None, reverse=False):
        if cmp_ is None:
            cmp_ = cmp
        if key and reverse:
            def sort(a, b):
                v = -cmp_(key(a), key(b))
                return JS("@{{v}}.__v")
        elif key:
            def sort(a, b):
                v = cmp_(key(a), key(b))
                return JS("@{{v}}.__v")
        elif reverse:
            def sort(a, b):
                v = -cmp_(a, b)
                return JS("@{{v}}.__v")
        else:
            def sort(a, b):
                v = cmp_(a, b)
                return JS("@{{v}}.__v")
        JS("@{{self}}.__array.sort(@{{sort}})")

    def getArray(self):
        """
        Access the javascript Array that is used internally by this list
        """
        return self.__array

    def __str__(self):
        JS("""
        var s = "[";
        for (var i=0; i < @{{self}}.__array.length; i++) {
            s += @{{repr}}(@{{self}}.__array[i]);
            if (i < @{{self}}.__array.length - 1)
                s += ", ";
        }
        s += "]";
        return B$str(s);
        """)

    def __repr__(self):
        JS("""
        var s = "[";
        for (var i=0; i < @{{self}}.__array.length; i++) {
            s += @{{repr}}(@{{self}}.__array[i]);
            if (i < @{{self}}.__array.length - 1)
                s += ", ";
        }
        s += "]";
        return B$str(s);
        """)

    def __add__(self, y):
        if not isinstance(y, list):
            raise TypeError("can only concatenate list to list")
        return list(self.__array.concat(y.__array))

    def __mul__(self, n):
        if not JS("@{{n}} !== null && @{{n}}.__number__ && (@{{n}}.__number__ != 0x01 || isFinite(@{{n}}))"):
            raise TypeError("can't multiply sequence by non-int")
        a = []
        while n:
            n -= 1
            a.extend(JS("@{{self}}.__array"))
        return a

    def __rmul__(self, n):
        return self.__mul__(n)

    # finally add builtin __slots__ for list
    JS("""${0,funcbase}$['__slots__'] = [];""")


# OK. I'm too lazy to implement dict.update() in javascript.
# Therefore it's added here.
def update(self, *args, **kwargs):
    if args:
        if len(args) > 1:
            raise TypeError("update expected at most 1 arguments, got %d" % len(args))
        d = args[0]
        if hasattr(d, "iteritems"):
            for k,v in d.iteritems():
                self[k] = v
        elif hasattr(d, "keys"):
            for k in d:
                self[k] = d[k]
        else:
            for k, v in d:
                self[k] = v
    if kwargs:
        for k,v in kwargs.iteritems():
            self[k] = v
dict.update = update
del update

def globals(obj):
    o = JS("{}")
    JS("""
    for (k in @{{obj}}) {
        if (@{{obj}}[k] != "undefined" && @{{obj}}[k] !== ${0,builtin}$[k]) {
            @{{o}}[k] = @{{obj}}[k];
        }
    }""")
    d = JS("B$dict(@{{o}}, true)")
    JS("@{{d}}.__object = @{{obj}}")
    def globals():
        return d
    return globals

def locals(obj):
    o = JS("{}")
    JS("""
    for (k in @{{obj}}) {
        if (typeof @{{obj}}[k] != "undefined") {
            @{{o}}[k] = @{{obj}}[k];
        }
    }""")
    d = JS("B$dict(@{{o}}, true)")
    JS("@{{d}}.__object = @{{obj}}")
    def locals():
        return d
    return locals



class set(object):
    def __init__(self, _data=None):
        """ Transform data into an array with [key,value] and add set
            self.__object
            Input data can be Array(key, val), iteratable (key,val) or
            Object/Function
        """
        JS("""
        var data = @{{_data}} === ${0,None}$ ? [] : @{{_data}};
        if (@{{isSet}}(data)) {
            @{{self}}.__object = {};
            var selfObj = @{{self}}.__object,
                dataObj = data.__object;
            for (var sVal in dataObj) {
                selfObj[sVal] = dataObj[sVal];
            }
            return ${0,None}$;
        }
        var item,
            i,
            n,
            selfObj = @{{self}}.__object = {};

        if (@{{!data}} instanceof Array) {
            // data is already an Array.
            // We deal with the Array of data after this if block.
        }

        // We may have some other set-like thing with __object
        else if (typeof @{{!data}}.__object == 'object') {
            var dataObj = @{{!data}}.__object;
            for (var sKey in dataObj) {
                selfObj[sKey] = dataObj[sKey];
            }
            return ${0,None}$;
        } else {
            var iter = @{{fcall}}(this, null, @{{iter}}, null, data),
                next = @{{getattr}}(iter, 'next'),
                stopiter = @{{stopiter}},
                v;
            data = [];
            for (var i = 0;; i++) {
                @{{stopiter}} = true;
                v = @{{fcall}}(this, null, next, iter);
                @{{stopiter}} = stopiter;
                if (v === @{{StopIter}}) {
                    break;
                }
                data[i] = v;
            }
        }
        // Assume uniform array content...
        if ((n = @{{!data}}.length) == 0) {
            return ${0,None}$;
        }
        i = n-1;
        do {
            item = @{{!data}}[i];
            selfObj[@{{hash}}(item)] = item;
        }
        while (i--);
        return ${0,None}$;
        """)

    def __cmp__(self, other):
        # We (mis)use cmp here for the missing __gt__/__ge__/...
        # if self == other : return 0
        # if self is subset of other: return -1
        # if self is superset of other: return 1
        # else return None
        if not isSet(other):
            return None
            #other = frozenset(other)
        JS("""
        var selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object,
            selfMismatch = false,
            otherMismatch = false;
        if (selfObj === otherObj) {
            @{{raise}}($new(@{{TypeError}}, B$str("Set operations must use two sets.")));
            }
        for (var sVal in selfObj) {
            if (!(sVal in otherObj)) {
                selfMismatch = true;
                break;
            }
        }
        for (var sVal in otherObj) {
            if (!(sVal in selfObj)) {
                otherMismatch = true;
                break;
            }
        }
        if (selfMismatch && otherMismatch) return ${0,None}$
        if (selfMismatch) return @{{int_one}};
        if (otherMismatch) return @{{int_minus_one}};
        return @{{int_zero}};
""")

    def __contains__(self, value):
        JS("""
        if (@{{isSet}}(@{{value}}) == 1) {
            // An instance of set
            // Use frozenset hash
            var hashes = new Array(),
                obj = @{{self}}.__object,
                i = 0;
            for (var v in obj) {
                hashes[i++] = v;
            }
            hashes.sort();
            var h = hashes.join("|");
            return (h in obj);
        }
        return @{{hash}}(@{{value}}) in @{{self}}.__object ? @{{True}} : @{{False}};
""")

    def __hash__(self):
        raise TypeError("set objects are unhashable")

    def __iter__(self):
        JS("""
        var items = new Array(),
            i = 0,
            obj = @{{self}}.__object;
        for (var key in obj) {
            items[i++] = obj[key];
        }
        return new $iter_array(items);
        """)

    def __len__(self):
        size=0.0
        JS("""
        for (var i in @{{self}}.__object) @{{size}}++;
        """)
        return int(size)

    #def __str__(self):
    #    return self.__repr__()
    #See monkey patch at the end of the set class definition

    def __repr__(self):
        if callable(self):
            return "<type '%s'>" % self.__name__
        JS("""
        var values = new Array();
        var i = 0,
            obj = @{{self}}.__object,
            s = @{{self}}['__class__']['__name__'] + "([";
        for (var sVal in obj) {
            values[i++] = @{{repr}}(obj[sVal]);
        }
        s += values.join(", ");
        s += "])";
        return B$str(s);
        """)

    def __and__(self, other):
        """ Return the intersection of two sets as a new set.
            only available under --number-classes
        """
        if not isSet(other):
            return NotImplemented
        return self.intersection(other)

    def __or__(self, other):
        """ Return the union of two sets as a new set..
            only available under --number-classes
        """
        if not isSet(other):
            return NotImplemented
        return self.union(other)

    def __xor__(self, other):
        """ Return the symmetric difference of two sets as a new set..
            only available under --number-classes
        """
        if not isSet(other):
            return NotImplemented
        return self.symmetric_difference(other)

    def  __sub__(self, other):
        """ Return the difference of two sets as a new Set..
            only available under --number-classes
        """
        if not isSet(other):
            return NotImplemented
        return self.difference(other)

    def add(self, value):
        JS("""@{{self}}.__object[@{{hash}}(@{{value}})] = @{{value}};""")
        return None

    def clear(self):
        JS("""@{{self}}.__object = {};""")
        return None

    def copy(self):
        new_set = set()
        JS("""
        var obj = @{{new_set}}.__object,
            selfObj = @{{self}}.__object;
        for (var sVal in selfObj) {
            obj[sVal] = selfObj[sVal];
        }
""")
        return new_set

    def difference(self, other):
        """ Return the difference of two sets as a new set.
            (i.e. all elements that are in this set but not the other.)
        """
        if not isSet(other):
            other = frozenset(other)
        new_set = set()
        JS("""
        var obj = @{{new_set}}.__object,
            selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            if (!(sVal in otherObj)) {
                obj[sVal] = selfObj[sVal];
            }
        }
""")
        return new_set

    def difference_update(self, other):
        """ Remove all elements of another set from this set.
        """
        if not isSet(other):
            other = frozenset(other)
        JS("""
        var selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in otherObj) {
            if (sVal in selfObj) {
                delete selfObj[sVal];
            }
        }
""")
        return None

    def discard(self, value):
        if isSet(value) == 1:
            value = frozenset(value)
        JS("""delete @{{self}}.__object[@{{hash}}(@{{value}})];""")
        return None

    def intersection(self, other):
        """ Return the intersection of two sets as a new set.
            (i.e. all elements that are in both sets.)
        """
        if not isSet(other):
            other = frozenset(other)
        new_set = set()
        JS("""
        var obj = @{{new_set}}.__object,
            selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            if (sVal in otherObj) {
                obj[sVal] = selfObj[sVal];
            }
        }
""")
        return new_set

    def intersection_update(self, other):
        """ Update a set with the intersection of itself and another.
        """
        if not isSet(other):
            other = frozenset(other)
        JS("""
        var selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            if (!(sVal in otherObj)) {
                delete selfObj[sVal];
            }
        }
""")
        return None

    def isdisjoint(self, other):
        """ Return True if two sets have a null intersection.
        """
        if not isSet(other):
            other = frozenset(other)
        JS("""
        var selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            if (typeof otherObj[sVal] != "undefined") {
                return @{{False}};
            }
        }
        for (var sVal in otherObj) {
            if (typeof selfObj[sVal] != "undefined") {
                return @{{False}};
            }
        }
""")
        return True

    def issubset(self, other):
        if not isSet(other):
            other = frozenset(other)
        v = self.__cmp__(other)
        if v is None:
            return False
        return v < 0
        return JS("@{{v}}.__v < 0 ? @{{True}} : @{{False}}")
        return JS("@{{self}}.__cmp__(@{{other}}).valueOf() < 0 ? @{{True}} : @{{False}}")

    def issuperset(self, other):
        if not isSet(other):
            other = frozenset(other)
        v = self.__cmp__(other)
        if v is None:
            return False
        return v >= 0
        return JS("@{{v}}.valueOf()|1 == 1 ? @{{True}} : @{{False}}")
        return JS("(@{{self}}.__cmp__(@{{other}}).valueOf()|1) == 1 ? @{{True}} : @{{False}}")

    def pop(self):
        JS("""
        for (var sVal in @{{self}}.__object) {
            var value = @{{self}}.__object[sVal];
            delete @{{self}}.__object[sVal];
            return value;
        }
        """)
        raise KeyError("pop from an empty set")

    def remove(self, value):
        JS("""
        var val = @{{isSet}}(@{{value}}) == 1 ? @{{frozenset}}(@{{value}}) : @{{value}};
        var h = @{{hash}}(val);
        if (!(h in @{{self}}.__object)) {
            return @{{raise}}($new(@{{KeyError}}, @{{value}}));
        }
        delete @{{self}}.__object[h];
        """)

    def symmetric_difference(self, other):
        """ Return the symmetric difference of two sets as a new set.
            (i.e. all elements that are in exactly one of the sets.)
        """
        if not isSet(other):
            other = frozenset(other)
        new_set = set()
        JS("""
        var obj = @{{new_set}}.__object,
            selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            if (typeof otherObj[sVal] == "undefined") {
                obj[sVal] = selfObj[sVal];
            }
        }
        for (var sVal in otherObj) {
            if (typeof selfObj[sVal] == "undefined") {
                obj[sVal] = otherObj[sVal];
            }
        }
""")
        return new_set

    def symmetric_difference_update(self, other):
        """ Update a set with the symmetric difference of itself and another.
        """
        if not isSet(other):
            other = frozenset(other)
        JS("""
        var obj = new Object(),
            selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            if (typeof otherObj[sVal] == "undefined") {
                obj[sVal] = selfObj[sVal];
            }
        }
        for (var sVal in otherObj) {
            if (typeof selfObj[sVal] == "undefined") {
                obj[sVal] = otherObj[sVal];
            }
        }
        @{{self}}.__object = obj;
""")
        return None

    def union(self, other):
        """ Return the union of two sets as a new set.
            (i.e. all elements that are in either set.)
        """
        new_set = set()
        if not isSet(other):
            other = frozenset(other)
        JS("""
        var obj = @{{new_set}}.__object,
            selfObj = @{{self}}.__object,
            otherObj = @{{other}}.__object;
        for (var sVal in selfObj) {
            obj[sVal] = selfObj[sVal];
        }
        for (var sVal in otherObj) {
            if (!(sVal in selfObj)) {
                obj[sVal] = otherObj[sVal];
            }
        }
""")
        return new_set

    def update(self, data):
        if not isSet(data):
            data = frozenset(data)
        JS("""
        var selfObj = @{{self}}.__object,
            dataObj = @{{data}}.__object;
        for (var sVal in dataObj) {
            if (!(sVal in selfObj)) {
                selfObj[sVal] = dataObj[sVal];
            }
        }
        """)
        return None

    __str__ = __repr__


class frozenset(set):
    def __init__(self, _data=None):
        if JS("(!('__object' in @{{self}}))"):
            set.__init__(self, _data)

    def __hash__(self):
        JS("""
        var hashes = new Array(), obj = @{{self}}.__object, i = 0;
        for (var v in obj) {
            hashes[i++] = v;
        }
        hashes.sort();
        return (@{{self}}.$H = '#frozenset#' + hashes.join("|"));
""")

    def add(self, value):
        raise AttributeError('frozenset is immutable')

    def clear(self):
        raise AttributeError('frozenset is immutable')

    def difference_update(self, other):
        raise AttributeError('frozenset is immutable')

    def discard(self, value):
        raise AttributeError('frozenset is immutable')

    def intersection_update(self, other):
        raise AttributeError('frozenset is immutable')

    def pop(self):
        raise AttributeError('frozenset is immutable')

    def symmetric_difference_update(self, other):
        raise AttributeError('frozenset is immutable')

frozenset.__repr__ = set.__repr__
frozenset.__str__ = set.__str__


class property(object):
    # From: http://users.rcn.com/python/download/Descriptor.htm
    # Extended with setter(), deleter() and fget.__doc_ copy
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if not doc is None or not hasattr(fget, '__doc__') :
            self.__doc__ = doc
        else:
            self.__doc__ = fget.__doc__

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError, "unreadable attribute"
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError, "can't set attribute"
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError, "can't delete attribute"
        self.fdel(obj)

    def setter(self, fset):
        self.fset = fset
        return self

    def deleter(self, fdel):
        self.fdel = fdel
        return self


class enumerate(object):

    def __init__(self, iterable):
        self.iter = iter(iterable)
        self.index = 0

    def __iter__(self):
        return self

    def next(self):
        v = self.iter.next()
        if v is StopIter:
            return v
        v = (self.index, v)
        self.index += 1
        return v


# See e.g. http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
class super(object):
    _none = NoneType()

    def __init__(self, type, object_or_type = None):
        self.__thisclass__ = type
        if object_or_type is None:
            self.__self__ = self.__self_class__ = None
        else:
            if JS("!@{{_issubtype}}(@{{object_or_type}}, @{{type}})"):
                debugger()
                JS("@{{_issubtype}}(@{{object_or_type}}, @{{type}})")
                raise TypeError("super(type, obj): obj must be an instance or subtype of type")
            if JS("@{{object_or_type}}['$inst'] === true"):
                self.__self_class__ = object_or_type.__class__
            else:
                self.__self_class__ = object_or_type
            self.__self__ = object_or_type

    def __get__(self, obj, type=None):
        _thisclass = JS("@{{self}}['$dict']['__thisclass__']")
        _self = JS("@{{self}}['$dict']['__self__']")
        if _self is None and obj is not None:
            return super(_thisclass, obj)
        else:
            return self

    JS("""\
    func(${0,module}$, null, ${0,funcbase}$, '__getattribute__', ${0, function}$, ['self', 'attr'], null, null, null, function($self, $attr) {
        var _thisclass = $self['$dict']['__thisclass__'],
            _self = $self['$dict']['__self__'],
            starttype = $self['$dict']['__self_class__'],
            attr = $attr.valueOf(),
            _stopiter = @{{stopiter}},
            cls, iter, next, x, y;

${2, attr_args_validate, $self, $attr}$
        x = _self['$dict'][attr];
        if (_self['$inst'] !== true || typeof x == "undefined") {
            iter = @{{fcall}}(this, null, @{{iter}}, null, starttype['__mro__']);
            next = @{{getattr}}(iter, 'next');
            for (;;) {
                @{{stopiter}} = true;
                cls = @{{fcall}}(this, null, next, iter);
                @{{stopiter}} = _stopiter;
                if (cls === @{{StopIter}}) {
                    break;
                }
                if (cls === _thisclass) {
                    break;
                }
            }
            for (;;) {
                @{{stopiter}} = true;
                cls = @{{fcall}}(this, null, next, iter);
                @{{stopiter}} = _stopiter;
                if (cls === @{{StopIter}}) {
                    break;
                }
                x = cls['$dict'][attr];
                if (typeof x != "undefined") {
                    break;
                }
            }
        }
        if (typeof x != "undefined") {
            var none = $self['__class__']['$dict']['_none'];
            y = @{{_getattr}}(x, '__get__');
            if (typeof y != "undefined") {
                return y;
            }
${3, bind_method, x, x, _self, _thisclass}$
            return x;
        }
        return @{{raise}}($new(@{{AttributeError}}, B$str(attr)));
});""")

# taken from mochikit: range( [start,] stop[, step] )
def xrange(start, stop = None, step = 1):
    if stop is None:
        stop = start
        start = 0
    if not JS("@{{start}}!== null && @{{start}}.__number__ && (@{{start}}.__number__ != 0x01 || isFinite(@{{start}}))"):
        raise TypeError("xrange() integer start argument expected, got %s" % start.__class__.__name__)
    if not JS("@{{stop}}!== null && @{{stop}}.__number__ && (@{{stop}}.__number__ != 0x01 || isFinite(@{{stop}}))"):
        raise TypeError("xrange() integer end argument expected, got %s" % stop.__class__.__name__)
    if not JS("@{{step}}!== null && @{{step}}.__number__ && (@{{step}}.__number__ != 0x01 || isFinite(@{{step}}))"):
        raise TypeError("xrange() integer step argument expected, got %s" % step.__class__.__name__)
    rval = nval = start
    JS("""
    var nstep = (@{{stop}}-@{{start}})/@{{step}};
    nstep = nstep < 0 ? Math.ceil(nstep) : Math.floor(nstep);
    if ((@{{stop}}-@{{start}}) % @{{step}}) {
        nstep++;
    }
    var _stop = @{{start}}+ nstep * @{{step}};
    if (nstep <= 0) @{{nval}} = _stop;
    var x = {
        'next': function(noStop) {
            if (@{{nval}} == _stop) {
                if (noStop === true) {
                    return;
                }
                throw @{{StopIteration}};
            }
            @{{rval}} = @{{nval}};
            @{{nval}} += @{{step}};
""")
    return INT(rval);
    JS("""
        },
        '$genfunc': function() {
            return this.next(true);
        },
        '__iter__': function() {
            return this;
        },
        '__reversed__': function() {
            return @{{xrange}}(@{{!_stop}}-@{{step}}, @{{start}}-@{{step}}, -@{{step}});
        },
        'toString': function() {
            var s = "xrange(";
            if (@{{start}}!= 0) {
                s += @{{start}}+ ", ";
            }
            s += @{{!_stop}};
            if (@{{step}}!= 1) {
                s += ", " + @{{step}};
            }
            return s + ")";
        },
        '__repr__': function() {
            return "'" + this.toString() + "'";
        }
    };
    @{{!x}}['__str__'] = @{{!x}}.toString;
    return @{{!x}};
    """)

def range(start, stop = None, step = 1):
    if stop is None:
        stop = start
        start = 0
    i = start
    if not JS("@{{start}}!== null && @{{start}}.__number__ && (@{{start}}.__number__ != 0x01 || isFinite(@{{start}}))"):
        raise TypeError("xrange() integer start argument expected, got %s" % start.__class__.__name__)
    if not JS("@{{stop}}!== null && @{{stop}}.__number__ && (@{{stop}}.__number__ != 0x01 || isFinite(@{{stop}}))"):
        raise TypeError("xrange() integer end argument expected, got %s" % stop.__class__.__name__)
    if not JS("@{{step}}!== null && @{{step}}.__number__ && (@{{step}}.__number__ != 0x01 || isFinite(@{{step}}))"):
        raise TypeError("xrange() integer step argument expected, got %s" % step.__class__.__name__)
    items = JS("new Array()")
    JS("""
    var nstep = (@{{stop}}-@{{start}})/@{{step}};
    nstep = nstep < 0 ? Math.ceil(nstep) : Math.floor(nstep);
    if ((@{{stop}}-@{{start}}) % @{{step}}) {
        nstep++;
    }
    var _stop = @{{start}}+ nstep * @{{step}};
    if (nstep <= 0) @{{i}}= _stop;
    for (; @{{i}}!= _stop; @{{i}}+= @{{step}}) {
""")
    items.push(int(i))
    JS('}')
    return list(items)
xrange = range

def ord(x):
    JS("""
    if (typeof @{{x}} == "string") {
        if (@{{x}}.length == 1) {
            return $new(@{{int}}, @{{x}}.charCodeAt(0));
        }
    } else if (@{{x}}['$inst'] === true) {
        var s = @{{x}}['__s'];
        if (typeof s == "string") {
            if (s.length == 1) {
                return $new(@{{int}}, s.charCodeAt(0));
            }
        }
    }
    return @{{raise}}($new(@{{TypeError}}, B$str("ord() expected string of length 1")));
""")

def chr(x):
    JS("""
    return B$str(String.fromCharCode(@{{x}}.valueOf()));
    """)

def is_basetype(x):
    JS("""
       var t = typeof(@{{x}});
       return t == 'boolean' ||
       t == 'function' ||
       t == 'number' ||
       t == 'string' ||
       t == "undefined";
    """)

def get_pyjs_classtype(x):
    JS("""
        if (@{{x}}!== null && typeof @{{x}}.__is_instance__ == 'boolean') {
            var src = @{{x}}.__name__;
            return src;
        }
        return null;
    """)

_str_None = "None"
_str_True = "True"
_str_False = "False"
_str_zero = "0"
_str_one = "1"
_str_minus_one = "-1"
_str_undefined = "undefined"


JS("""
func(${0,module}$, null, ${0,g}$, 'repr', 1, ['object'], null, null, null, function($object) {
    switch ($object) {
        case null:
        case @{{None}}:
            return @{{_str_None}};
        case true:
        case @{{True}}:
            return @{{_str_True}};
        case false:
        case @{{False}}:
            return @{{_str_False}};
        case 0:
        case @{{int_zero}}:
            return @{{_str_zero}};
        case 1:
        case @{{int_one}}:
            return @{{_str_one}};
        case -1:
        case @{{int_zero}}:
            return @{{_str_minus_one}};
        default:
            var t = typeof($object);
            switch (t) {
                case "undefined":
                    return @{{_str_undefined}};
                case "string":
                    return B$str("'" + $object.replace("'", "\\'") + "'")
                case "number":
                    return B$str($object.toString())
            }
            switch ($object['__class__']) {
                case @{{str}}:
                    return B$str("'" + $object.__s.replace("'", "\\'") + "'")
                case @{{int}}:
                    return B$str($object.__v.toString());
            }
            var r = @{{_getattr}}($object, '__repr__');
            if (typeof r != "undefined") {
                return @{{fcall}}(this, null, r, $object);
            }
            switch (t) {
                case "function":
                    return B$str("<function '" + $object.name + "'>");
                case "object":
                    return B$str($object.toString());
            }
    }
    return B$str($object.toString());
});

func(${0,module}$, null, ${0,g}$, 'len', 1, ['object'], null, null, null, function($object) {
    if (typeof $object == "undefined") {
       @{{raise}}($new(@{{UndefinedValueError}}, B$str("len: object")));
    }
    if ($object === null)
        return @{{int_zero}};
    if (typeof $object.__array != "undefined")
        return $new(@{{int}}, $object.__array.length);
    if ($object.__class__ == @{{str}})
        return $new(@{{int}}, $object.__s.length);
    if (typeof $object._length != "undefined")
        return $new(@{{int}}, $object._length);
    if (typeof $object != 'function') {
        if (typeof $object.length != "undefined")
            return $new(@{{int}}, $object.length);
    }
    var v = @{{getattr}}($object, '__len__', ${0,None}$);
    if (v !== ${0,None}$)
       return @{{fcall}}(this, null, v, null);
    @{{raise}}($new(@{{TypeError}}, B$str("object has no len()")));
});

func(${0,module}$, null, ${0,g}$, 'isinstance', 1, ['object', 'classinfo'], null, null, null, function($object, $classinfo) {
    var mro, cls, classes = $classinfo['__class__'] === @{{tuple}} ? $classinfo['__array'] : $classinfo;
    if (typeof $object['$inst'] == "undefined") {
        return @{{False}};
    }
    if (!(classes instanceof Array)) {
        classes = [classes];
    }
    if (typeof $object != "undefined" && $object !== null) {
        cls = $object['__class__'];
        for (var i = 0; i < classes.length; i++) {
            if (classes[i]['$inst'] !== false) {
                if (classes[i]['__class__'] !== @{{tuple}}) {
                    @{{raise}}($new(@{{TypeError}}, B$str("isinstance() arg 2 must be a class, type, or tuple of classes and types")));
                }
                if (@{{isinstance}}($object, classes[i]) === @{{True}}) {
                    return @{{True}};
                }
            }
            mro = cls.__mro__;
            for (var j = 0; j < mro.length; j++) {
                if (mro[j] === classes[i]) {
                    return @{{True}};
                }
            }
        }
    }
    return @{{False}};
});

func(${0,module}$, null, ${0,g}$, 'issubclass', 1, ['class', 'classinfo'], null, null, null, function($class, $classinfo) {
    if (typeof $class == "undefined" || $class === null || $class['$inst'] !== false) {
        @{{raise}}($new(@{{TypeError}}, B$str("arg 1 must be a class")));
    }
    var mro, classes = $classinfo['__class__'] === @{{tuple}} ? $classinfo['__array'] : [$classinfo];
    mro = $class['__mro__'];
    for (var i = 0; i < classes.length; i++) {
        if (classes[i]['$inst'] !== false) {
            @{{raise}}($new(@{{TypeError}}, B$str("issubclass() arg 2 must be a class or tuple of classes")));
        }
        for (var j = 0; j < mro.length; j++) {
            if (mro[j] === classes[i]) {
                return @{{True}};
            }
        }
    }
    return @{{False}};
});

func(${0,module}$, null, ${0,g}$, '_issubclass', 1, ['class', 'classinfo'], null, null, null, function($class, $classinfo) {
    if (typeof $class == "undefined" || $class === null || $class['$inst'] !== false) {
        return false;
    }
    var mro, classes = $classinfo['__class__'] === @{{tuple}} ? $classinfo['__array'] : $classinfo instanceof Array ? $classinfo : [$classinfo];
    mro = $class['__mro__'];
    for (var i = 0; i < classes.length; i++) {
        if (classes[i]['$inst'] !== false) {
            return false;
        }
        for (var j = 0; j < mro.length; j++) {
            if (mro[j] === classes[i]) {
                return true;
            }
        }
    }
    return false;
});

func(${0,module}$, null, ${0,g}$, '_issubtype', 1, ['obj', 'type'], null, null, null, function($obj, $type) {
    if (typeof $obj == "undefined" || $obj === null || typeof $obj['$inst'] != 'boolean') {
        return false;
    }
    if (typeof $type == "undefined" || $type === null || typeof $type['$inst'] != 'boolean') {
        return false;
    }
    var mro = $obj['$inst'] === true ? $obj['__class__']['__mro__'] : $obj['__mro__'];
    for (var j = 0; j < mro.length; j++) {
        if (mro[j] === $type) {
            return true;
        }
    }
    return false;
});

func(${0,module}$, null, ${0,g}$, 'callable', 1, ['obj'], null, null, null, function($obj) {
    if (typeof $obj == 'function') {
        if ($obj['$inst'] !== true ||
            $obj['__class__'] === @{{function}} ||
            $obj['__class__'] === @{{instancemethod}}) {
            return @{{True}};
        }
        // Only check the $dict from the classes
        // and skip the class.__call__
        var mro = $obj['__class__']['__mro__'],
            f;
        for (var i = 0; i < mro.length; i++) {
            f = mro[i]['$dict']['__call__'];
            if (typeof f != "undefined") {
                break;
            }
        }
        if (typeof f == 'function') return @{{True}};
    }
    return @{{False}};
});

func(${0,module}$, null, ${0,g}$, 'dir', 1, ['obj'], null, null, null, function($obj) {
    if (typeof $obj == "undefined") {
        @{{raise}}($new(@{{UndefinedValueError}}, B$str("dir(obj)")));
    }
    var $dict, props = {},
        mro = $obj['$inst'] === true ? $obj['__class__']['__mro__'] : $obj['__mro__'];
    for (var i = mro.length - 1; i >= 0; i--) {
        $dict = mro[i]['$dict'];
        for (var a in $dict) {
            if (a.charAt(0) != '#') {
                props[a] = $dict[a];
            }
        }
    }
    if ($obj['$inst'] === true) {
        $dict = $obj['$dict'];
        for (var a in $dict) {
            if (a.charAt(0) != '#') {
                props[a] = $dict[a];
            }
        }
    }
    var properties = B$tuple();
    var __array = properties.__array;
    for (var a in props) {
        __array.push(B$str(a));
    }
    return properties;
});
""")

def filter(obj, method, sequence=None):
    # object context is LOST when a method is passed, hence object must be passed separately
    # to emulate python behaviour, should generate this code inline rather than as a function call
    items = []
    if sequence is None:
        sequence = method
        method = obj

        for item in sequence:
            if method(item):
                items.append(item)
    else:
        for item in sequence:
            if method.call(obj, item):
                items.append(item)

    return items


def map(obj, method, sequence=None):
    items = []

    if sequence is None:
        sequence = method
        method = obj

        for item in sequence:
            items.append(method(item))
    else:
        for item in sequence:
            items.append(method.call(obj, item))

    return items


def reduce(func, iterable, initializer=JS("null")):
    try:
        iterable = iter(iterable)
    except:
        raise TypeError, "reduce() arg 2 must support iteration"
    empty = True
    for value in iterable:
        empty = False
        if JS("""@{{initializer}} === null || typeof @{{initializer}} == "undefined" """):
            initializer = value
        else:
            initializer = func(initializer, value)
    if empty:
        if JS("""@{{initializer}} === null || typeof @{{initializer}} == "undefined" """):
            raise TypeError, "reduce() of empty sequence with no initial value"
        return initializer
    return initializer


def zip(*iterables):
    n = len(iterables)
    if n == 0:
        return []
    lst = []
    iterables = [iter(i) for i in iterables]
    try:
        while True:
            t = []
            i = 0
            while i < n:
                t.append(iterables[i].next())
                i += 1
            lst.append(tuple(t))
    except StopIteration:
        pass
    return lst


def sorted(iterable, cmp=None, key=None, reverse=False):
    lst = list(iterable)
    lst.sort(cmp, key, reverse)
    return lst


def reversed(iterable):
    if hasattr(iterable, '__reversed__'):
        return iterable.__reversed__()
    if hasattr(iterable, '__len__') and hasattr(iterable, '__getitem__'):
        if len(iterable) == 0:
            l = []
            return l.__iter__()
        try:
            v = iterable[0]
            return _reversed(iterable)
        except:
            pass
    raise TypeError("argument to reversed() must be a sequence")

def _reversed(iterable):
    rev = []
    for i in iterable:
        rev.insert(0, i)
    return rev
    #i = len(iterable)
    #while i > 0:
    #    i -= 1
    #    yield iterable[i]


def iter(iterable, sentinel=None):
    if sentinel is None:
        i = getattr(iterable, '__iter__', None)
        if i is not None:
            return i()
        i = getattr(iterable, '__getitem__', None)
        if i is not None:
            return _iter_getitem(iterable)
        if JS("@{{iterable}} instanceof Array"):
            return list(iterable).__iter__()
        raise TypeError("object is not iterable")
    if callable(iterable):
        return _iter_callable(iterable, sentinel)
    raise TypeError("iter(v, w): v must be callable")

def _iter_getitem(object):
    debugger() # not tested
    items = []
    i = -1
    try:
        i += 1
        items.append(object[i])
    except IndexError:
        pass
    return items
    #i = 0
    #try:
    #    while True:
    #        yield object[i]
    #        i += 1
    #except IndexError:
    #    pass

def _iter_callable(callable, sentinel):
    while True:
        nextval = callable()
        if nextval == sentinel:
            break
        #yield nextval

def min(*sequence):
    if len(sequence) == 1:
        sequence = sequence[0]
    minValue = None
    for item in sequence:
        if minValue is None:
            minValue = item
        elif cmp(item, minValue) == -1:
            minValue = item
    return minValue


def max(*sequence):
    if len(sequence) == 1:
        sequence = sequence[0]
    maxValue = None
    for item in sequence:
        if maxValue is None:
            maxValue = item
        elif cmp(item, maxValue) == 1:
            maxValue = item
    return maxValue

def sum(iterable, start=None):
    if start is None:
        start = 0
    for i in iterable:
        start += i
    return start


# type functions from Douglas Crockford's Remedial Javascript: http://www.crockford.com/javascript/remedial.html
def isObject(a):
    JS("""
    return (@{{a}}!== null && (typeof @{{a}}== 'object')) || typeof @{{a}}== 'function';
    """)

def isFunction(a):
    JS("""
    return typeof @{{a}}== 'function';
    """)

def isString(a):
    JS("""
    return typeof @{{a}}== 'string';
    """)

def isNull(a):
    JS("""
    return typeof @{{a}}== 'object' && !@{{a}};
    """)

def isArray(a):
    JS("""
    return @{{isObject}}(@{{a}}) && @{{a}}.constructor === Array;
    """)

def isUndefined(a):
    JS("""
    return typeof @{{a}}== "undefined";
    """)

def isIteratable(a):
    return hasattr(a, '__iter__')

def isNumber(a):
    JS("""
    return @{{a}}!== null && @{{a}}.__number__ &&
           (@{{a}}.__number__ != 0x01 || isFinite(@{{a}}));
    """)

def isInteger(a):
    JS("""
    switch (@{{a}}.__number__) {
        case 0x01:
            if (@{{a}} != Math.floor(@{{a}})) break;
        case 0x02:
        case 0x04:
            return true;
    }
    return false;
""")

def isSet(a):
    JS("""
    if (@{{a}}=== null) return false;
    if (typeof @{{a}}.__object == "undefined") return false;
    var a_mro = @{{a}}['__class__']['__mro__'];
    switch (a_mro[a_mro.length-2]) {
        case @{{set}}:
            return 1;
        case @{{frozenset}}:
            return 2;
    }
    return false;
""")
def toJSObjects(x):
    """
       Convert the pyjs pythonic list and dict objects into javascript Object and Array
       objects, recursively.
    """
    if isArray(x):
        JS("""
        var result = [];
        for(var k=0; k < @{{x}}.length; k++) {
           var v = @{{x}}[k];
           var tv = @{{toJSObjects}}(v);
           result.push(tv);
        }
        return result;
        """)
    if isObject(x):
        if x.__number__:
            return x.valueOf()
        elif isinstance(x, dict):
            JS("""
            var o = @{{x}}.getObject();
            var result = {};
            for (var i in o) {
               result[o[i][0].toString()] = @{{toJSObjects}}(o[i][1]);
            }
            return result;
            """)
        elif isinstance(x, list):
            return toJSObjects(x.__array)
        elif hasattr(x, '__class__'):
            # we do not have a special implementation for custom
            # classes, just pass it on
            return x
    if isObject(x):
        JS("""
        var result = {};
        for(var k in @{{x}}) {
            var v = @{{x}}[k];
            var tv = @{{toJSObjects}}(v);
            result[k] = tv;
            }
            return result;
         """)
    if isString(x):
        return str(x)
    return x

def sprintf(strng, args):
    # See http://docs.python.org/library/stdtypes.html
    JS(r"""
    var re_dict = /([^%]*)%[(]([^)]+)[)]([#0\x20\x2B-]*)(\d+)?(\.\d+)?[hlL]?(.)((.|\n)*)/;
    var re_list = /([^%]*)%([#0\x20\x2B-]*)(\*|(\d+))?(\.\d+)?[hlL]?(.)((.|\n)*)/;
    var re_exp = /(.*)([+-])(.*)/;

    var argidx = 0;
    var nargs = 0;
    var result = [];
    var strng = @{{strng}}.valueOf();
    var remainder = strng;

    function formatarg(flags, minlen, precision, conversion, param) {
        var subst = '';
        var numeric = true;
        var left_padding = 1;
        var padchar = ' ';
        if (minlen === null || minlen == 0 || !minlen) {
            minlen=0;
        } else {
            minlen = parseInt(minlen);
        }
        if (!precision) {
            precision = null;
        } else {
            precision = parseInt(precision.substr(1));
        }
        if (flags.indexOf('-') >= 0) {
            left_padding = 0;
        }
        switch (conversion) {
            case '%':
                numeric = false;
                subst = '%';
                break;
            case 'c':
                numeric = false;
                subst = String.fromCharCode(parseInt(param.toString()));
                break;
            case 'd':
            case 'i':
            case 'u':
                subst = '' + parseInt(param.toString());
                break;
            case 'e':
                if (precision === null) {
                    precision = 6;
                }
                subst = re_exp.exec(String(param.valueOf().toExponential(precision)));
                if (subst[3].length == 1) {
                    subst = subst[1] + subst[2] + '0' + subst[3];
                } else {
                    subst = subst[1] + subst[2] + subst[3];
                }
                break;
            case 'E':
                if (precision === null) {
                    precision = 6;
                }
                subst = re_exp.exec(String(param.valueOf().toExponential(precision)).toUpperCase());
                if (subst[3].length == 1) {
                    subst = subst[1] + subst[2] + '0' + subst[3];
                } else {
                    subst = subst[1] + subst[2] + subst[3];
                }
                break;
            case 'f':
                if (precision === null) {
                    precision = 6;
                }
                subst = String(parseFloat(param.toString()).toFixed(precision));
                break;
            case 'F':
                if (precision === null) {
                    precision = 6;
                }
                subst = String(parseFloat(param.toString()).toFixed(precision)).toUpperCase();
                break;
            case 'g':
                if (precision === null && flags.indexOf('#') >= 0) {
                    precision = 6;
                }
                var p = param.valueOf();
                if (p >= 1E6 || p < 1E-5) {
                    subst = String(precision === null ? p.toExponential() : p.toExponential().toPrecision(precision));
                } else {
                    subst = String(precision === null ? parseFloat(p) : parseFloat(p).toPrecision(precision));
                }
                break;
            case 'G':
                if (precision === null && flags.indexOf('#') >= 0) {
                    precision = 6;
                }
                var p = param.valueOf();
                if (p >= 1E6 || p < 1E-5) {
                    subst = String(precision === null ? p.toExponential() : p.toExponential().toPrecision(precision)).toUpperCase();
                } else {
                    subst = String(precision === null ? parseFloat(p) : parseFloat(p).toPrecision(precision)).toUpperCase().toUpperCase();
                }
                break;
            case 'r':
                numeric = false;
                subst = @{{repr}}(param).__s;
                break;
            case 's':
                numeric = false;
                subst = $new(@{{str}}, param).__s;
                break;
            case 'o':
                subst = param.valueOf().toString(8);
                if (subst != '0' && flags.indexOf('#') >= 0) {
                    subst = '0' + subst;
                }
                break;
            case 'x':
                subst = param.valueOf().toString(16);
                if (flags.indexOf('#') >= 0) {
                    if (left_padding) {
                        subst = @{{str}}['$dict']['rjust'](subst, minlen - 2, '0').__s;
                    }
                    subst = '0x' + subst;
                }
                break;
            case 'X':
                subst = param.valueOf().toString(16).toUpperCase();
                if (flags.indexOf('#') >= 0) {
                    if (left_padding) {
                        subst = @{{str}}['$dict']['rjust'](subst, minlen - 2, '0').__s;
                    }
                    subst = '0x' + subst;
                }
                break;
            default:
                @{{raise}}($new(@{{ValueError}}, B$str("unsupported format character '" + conversion + "' ("+@{{hex}}(conversion.charCodeAt(0))+") at index " + (@{{strng}}.valueOf().length - remainder.length - 1))));
        }
        if (minlen && subst.length < minlen) {
            if (numeric && left_padding && flags.indexOf('0') >= 0) {
                padchar = '0';
            }
            subst = @{{str}}['$dict'][left_padding ? 'rjust' : 'ljust'](subst, minlen, padchar).__s;
        }
        return subst;
    }

    function sprintf_list(strng, args) {
        var a, left, flags, precision, conversion, minlen, param,
            __array = result;
        while (remainder) {
            a = re_list.exec(remainder);
            if (a === null) {
                __array[__array.length] = remainder;
                break;
            }
            left = a[1]; flags = a[2];
            minlen = a[3]; precision = a[5]; conversion = a[6];
            remainder = a[7];
            if (typeof minlen == "undefined") minlen = null;
            if (typeof precision == "undefined") precision = null;
            if (typeof conversion == "undefined") conversion = null;
            __array[__array.length] = left;
            if (minlen == '*') {
                if (argidx == nargs) {
                    @{{raise}}($new(@{{TypeError}}, B$str("not enough arguments for format string")));
                }
                minlen = args['__class__']['$dict']['__getitem__'](args, argidx++);
                switch (minlen.__number__) {
                    case 0x02:
                    case 0x04:
                        break;
                    case 0x01:
                        if (minlen == Math.floor(minlen)) {
                            break;
                        }
                    default:
                        @{{raise}}($new(@{{TypeError}}, B$str("* wants int")));
                }
            }
            if (conversion != '%') {
                if (argidx == nargs) {
                    @{{raise}}($new(@{{TypeError}}, B$str("not enough arguments for format string")));
                }
                param = args['__class__']['$dict']['__getitem__'](args, argidx++);
            }
            __array[__array.length] = formatarg(flags, minlen, precision, conversion, param);
        }
    }

    function sprintf_dict(strng, args) {
        var a = null,
            left = null,
            flags = null,
            precision = null,
            conversion = null,
            minlen = null,
            minlen_type = null,
            key = null,
            arg = args,
            param,
            __array = result;

        argidx++;
        while (remainder) {
            a = re_dict.exec(remainder);
            if (a === null) {
                __array[__array.length] = remainder;
                break;
            }
            left = a[1]; key = a[2]; flags = a[3];
            minlen = a[4]; precision = a[5]; conversion = a[6];
            remainder = a[7];
            if (typeof minlen == "undefined") minlen = null;
            if (typeof precision == "undefined") precision = null;
            if (typeof conversion == "undefined") conversion = null;
            __array[__array.length] = left;
            param = arg['__class__']['$dict']['__getitem__'](arg, key);
            __array[__array.length] = formatarg(flags, minlen, precision, conversion, param);
        }
    }

    if (strng.indexOf("%(") >= 0) {
        if (re_dict.exec(strng) !== null) {
            if (@{{args}}['__class__'] !== @{{dict}}) {
                @{{raise}}($new(@{{TypeError}}, B$str("format requires a mapping")));
            }
            sprintf_dict(strng, @{{args}});
            return B$str(result.join(""));
        }
    }
    if (@{{args}}['__class__'] !== @{{tuple}}) {
        if (@{{args}} instanceof Array) {
            @{{args}} = B$tuple(@{{args}});
        } else {
            @{{args}} = B$tuple([@{{args}}]);
        }
    }
    nargs = @{{args}}.__array.length;
    sprintf_list(strng, @{{args}});
    if (argidx != nargs) {
        @{{raise}}($new(@{{TypeError}}, B$str("not all arguments converted during string formatting")));
    }
    return B$str(result.join(""));
""")

def debugReport(msg):
    JS("""
    alert(@{{msg}}.valueOf());
    """)

JS("""
var $printFunc = null;
if (   typeof $wnd.console != "undefined"
    && typeof $wnd.console.debug == 'function') {
    $printFunc = function(s) {
        $wnd.console.debug(s);
    };
} else if (   typeof $wnd.opera != "undefined"
           && typeof $wnd.opera.postError == 'function') {
    $printFunc = function(s) {
        $wnd.opera.postError(s);
    };
} else if ( typeof console != "undefined") {
    if (   typeof console.log == 'function'
        || typeof console.log == 'object') {
        $printFunc = function(s) {
            console.log(s);
        };
    }
}
""")

def _print_to_console(s):
    JS("""
    if ($printFunc === null) return null;
    $printFunc(@{{s}}.valueOf());
    """)

def printFunc(objs, newline):
    # objs is an javascript array
    # newline is javascript true/false
    s = ' '.join([str(i) for i in list(objs)])
    if newline:
        s += '\n'
    sys.stdout.write(s);

def pow(x, y, z = None):
    p = None
    JS("@{{p}} = Math.pow(@{{x}}, @{{y}});")
    if z is None:
        return float(p)
    return float(p % z)

def hex(x):
    JS("""
    if (typeof @{{x}} == 'number') {
        if (Math.floor(@{{x}}) == @{{x}}) {
            return B$str('0x' + @{{x}}.toString(16));
        }
    } else {
        switch (@{{x}}.__number__) {
            case 0x02:
                return B$str('0x' + @{{x}}.__v.toString(16));
            case 0x04:
                return @{{long}}['$dict']['__hex__'](@{{x}});
        }
    }
""")
    raise TypeError("hex() argument can't be converted to hex")

def oct(x):
    JS("""
    if (typeof @{{x}} == 'number') {
        if (Math.floor(@{{x}}) == @{{x}}) {
            return B$str(@{{x}} == 0 ? '0': '0' + @{{x}}.toString(8));
        }
    } else {
        switch (@{{x}}.__number__) {
            case 0x02:
                return B$str(@{{x}}.__v == 0 ? '0': '0' + @{{x}}.__v.toString(8));
            case 0x04:
                return @{{long}}['$dict']['__oct__'](@{{x}});
        }
    }
""")
    raise TypeError("oct() argument can't be converted to oct")

def round(x, n = 0):
    n = pow(10, n)
    r = None
    JS("@{{r}} = Math.round(@{{n}}*@{{x}})/@{{n}};")
    return float(r)

def divmod(x, y):
    JS("""
    if (@{{x}} !== null && @{{y}} !== null) {
        switch ((@{{x}}.__number__ << 8) | @{{y}}.__number__) {
            case 0x0101:
            case 0x0104:
            case 0x0401:
                if (@{{y}} == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("float divmod()")));
                var f = Math.floor(@{{x}} / @{{y}});
                return @{{tuple}}([f, @{{x}} - f * @{{y}}]);
            case 0x0102:
                if (@{{y}}.__v == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("float divmod()")));
                var f = Math.floor(@{{x}} / @{{y}}.__v);
                return @{{tuple}}([f, @{{x}} - f * @{{y}}.__v]);
            case 0x0201:
                if (@{{y}} == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("float divmod()")));
                var f = Math.floor(@{{x}}.__v / @{{y}});
                return @{{tuple}}([f, @{{x}}.__v - f * @{{y}}]);
            case 0x0202:
                if (@{{y}}.__v == 0) @{{raise}}($new(@{{ZeroDivisionError}}, B$str("integer division or modulo by zero")));
                var f = Math.floor(@{{x}}.__v / @{{y}}.__v);
                return @{{tuple}}([new @{{int}}(f), new @{{int}}(@{{x}}.__v - f * @{{y}}.__v)]);
            case 0x0204:
                return @{{long}}['$dict']['__rdivmod__'](@{{y}}, @{{long}}(@{{x}}.__v));
            case 0x0402:
                return @{{long}}['$dict']['__divmod__'](@{{x}}, @{{long}}(@{{y}}.__v));
            case 0x0404:
                return @{{long}}['$dict']['__divmod__'](@{{x}}, @{{y}});
        }
        if (!@{{x}}.__number__) {
            if (   !@{{y}}.__number__
                && @{{x}}.__mro__.length > @{{y}}.__mro__.length
                && @{{isinstance}}(@{{x}}, @{{y}})
                && typeof @{{x}}['__divmod__'] == 'function')
                return @{{y}}.__divmod__(@{{x}});
            if (typeof @{{x}}['__divmod__'] == 'function') return @{{x}}.__divmod__(@{{y}});
        }
        if (!@{{y}}.__number__ && typeof @{{y}}['__rdivmod__'] == 'function') return @{{y}}.__rdivmod__(@{{x}});
    }
""")
    raise TypeError("unsupported operand type(s) for divmod(): '%r', '%r'" % (x, y))

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

__iter_prepare = JS("""function(iter, reuse_tuple) {

    if (typeof iter == "undefined") {
        @{{raise}}($new(@{{TypeError}}, B$str("iter is undefined")));
    }
    var it = {};
    it.$iter = iter;
    it.$loopvar = 0;
    it.$reuse_tuple = reuse_tuple;
    if (typeof (it.$arr = iter.__array) != "undefined") {
        it.$gentype = 0;
    } else {
        it.$iter = iter.__iter__();
        it.$gentype = typeof (it.$arr = iter.__array) != "undefined"? 0 : (typeof iter.$genfunc == 'function'? 1 : -1);
    }
    return it;
}""")

__wrapped_next = JS("""function(it) {
    var iterator = it.$iter;
    it.$nextval = it.$gentype?(it.$gentype > 0?
        iterator.next(true,it.$reuse_tuple):@{{wrapped_next}}(iterator)
                              ) : it.$arr[it.$loopvar++];
    return it;
}""")

# For optimized for loops: fall back for userdef iterators
wrapped_next = JS("""function (iter) {
    try {
        var res = iter.next();
    } catch (e) {
        if (e === @{{StopIteration}}) {
            return;
        }
        throw e;
    }
    return res;
}""")

_handle_exception = JS("""function(err) {
    $pyjs.loaded_modules['sys'].save_exception_stack();

    if (!$pyjs.in_try_except) {
        var $pyjs_msg = $pyjs.loaded_modules['sys']._get_traceback(err);
        $pyjs.__active_exception_stack__ = null;
        @{{printFunc}}([$pyjs_msg], true);
        @{{debugReport}}($pyjs_msg);
    }
    throw err;
};
""")
init()

a = {'a': 1}
for b in a.iteritems():
    pass

class Foo(object):
    a = 0
    def fn(self):
        return 'fn'
f = Foo()
s = str(f.fn)
max_i = 1#000000#
if max_i > 10:
    JS("alert('start')")
    t1 = JS("new Date()")
i = 0
while i < max_i:
    i = i + 1
    f = Foo()
    f.a = i
if max_i > 10:
    t2 = JS("new Date()")
    JS("alert('end ' + (@{{t2}}.getTime() - @{{t1}}.getTime()) / 1000.0)")
for i in [1]:
    a = 1

# Taken from importlib and slighly adapted:
# - _cgd_import
# - __import__

def _gcd_import(name, package=None, level=0):
    if package:
        if not hasattr(package, 'rindex'):
            raise ValueError("__package__ not set to a string")
        elif package not in sys.modules:
            msg = "Parent module '%s' not loaded, cannot perform relative import"
            raise SystemError(msg % package)
    if not name and level == 0:
        raise ValueError("Empty module name")
    if level > 0:
        dot = len(package)
        for x in range(level, 1, -1):
            try:
                dot = package.rindex('.', 0, dot)
            except ValueError:
                raise ValueError("attempted relative import beyond top-level package")
        if name:
            name = "%s.%s" % (package[:dot], name)
        else:
            name = package[:dot]
    if name in sys.modules:
        module = sys.modules[name]
        if module is None:
            message = "import of %s halted; None in sys.modules" % name
            raise ImportError(message)
        return module
    parent = name.rpartition('.')[0]
    path = None
    if parent:
        if parent not in sys.modules:
            _gcd_import(parent)
        # Backwards-compatibility; be nicer to skip the dict lookup.
        parent_module = sys.modules[parent]
        try:
            path = parent_module.__path__
        except AttributeError:
            msg = "No module named '%s'; %s is not a package" % (name, parent)
            #raise ImportError(msg)
    finder = imp.find_module(name, path)
    if finder is not None:
        module = imp.load_module(name, *finder)
    else:
        raise ImportError("No module named '%s'" % name)
    if parent:
        # Set the module as an attribute on its parent.
        setattr(parent_module, name.rpartition('.')[2], module)
    # Set __package__ if the loader did not.
    if not hasattr(module, '__package__') or module.__package__ is None:
        # Watch out for what comes out of sys.modules to not be a module,
        # e.g. an int.
        try:
            module.__package__ = module.__name__
            if not hasattr(module, '__path__'):
                module.__package__ = module.__package__.rpartition('.')[0]
        except AttributeError:
            pass
    return module

def __import__(name, globals={}, locals={}, fromlist=[], level=0):
    """Import a module.

    The 'globals' argument is used to infer where the import is occuring from
    to handle relative imports. The 'locals' argument is ignored. The
    'fromlist' argument specifies what should exist as attributes on the module
    being imported (e.g. ``from module import <fromlist>``).  The 'level'
    argument represents the package location to import from in a relative
    import (e.g. ``from ..pkg import mod`` would have a 'level' of 2).

    """
    if not hasattr(name, 'rpartition'):
        raise TypeError("module name must be str, not %s" %type(name))
    if level == 0:
        module = _gcd_import(name)
    else:
        # __package__ is not guaranteed to be defined or could be set to None
        # to represent that its proper value is unknown
        package = globals.get('__package__')
        if package is None:
            package = globals['__name__']
            if '__path__' not in globals:
                package = package.rpartition('.')[0]
        if level > 0:
            module = _gcd_import(name, package, level)
        else:
            try:
                module = _gcd_import(name, package, 1)
            except ImportError:
                module = _gcd_import(name, package, 0)
    # The hell that is fromlist ...
    if not fromlist:
        # Return up to the first dot in 'name'. This is complicated by the fact
        # that 'name' may be relative.
        if level == 0:
            return sys.modules[name.partition('.')[0]]
        elif not name:
            return module
        else:
            cut_off = len(name) - len(name.partition('.')[0])
            if cut_off == 0:
                return sys.modules[module.__name__]
            return sys.modules[module.__name__[:-cut_off]]
    else:
        # If a package was imported, try to import stuff from fromlist.
        if '*' in fromlist and hasattr(module, '__all__'):
            fromlist[:] = list(fromlist)
            fromlist.remove('*')
            fromlist.extend(module.__all__)
        none = NoneType()
        for x in fromlist:
            # In cPython we would do
            # if not hasattr(module, x):
            # we've got the globals and builtins mixed up
            y1 = getattr(module, x, none)
            y2 = getattr(__builtins__, x, none)
            if y1 is none or (module is not __builtins__ and y1 is y2):
                try:
                    subname = '%s.%s' % (module.__name__, x)
                    _gcd_import(subname)
                    #_gcd_import('%s.%s' % (module.__name__, x))
                except ImportError:
                    pass
    return module

# The import function is used in the translated code
def _import(assnames, name, globals, locals, fromlist, level):
    name = JS("typeof @{{name}} == 'string' ? $new(@{{str}}, @{{name}}) : @{{name}}")
    _globals = JS("B$dict(@{{globals}})")
    _fromlist = JS("B$list(@{{fromlist}})")
    _level = JS("@{{level}} === null ? @{{int_minus_one}} : $new(@{{int}}, @{{level}});")
    star = '*'
    module = None
    module = __import__(name, _globals, None, _fromlist, _level)
    JS("""
    var name, v, src, dst;
    for (var i = 0; i < @{{assnames}}.length; i++) {
        src = @{{assnames}}[i][0];
        dst = @{{assnames}}[i][1];
        name = src.valueOf()
        if (name.valueOf() != '*') {
            v = @{{module}}['$dict'][name];
            if (typeof v == "undefined") {
                @{{raise}}($new(@{{ImportError}}, B$str("cannot import name " + name)));
            }
            @{{locals}}[dst === null ? name : dst.valueOf()] = v;
        } else if (@{{list}}['$dict']['count'](@{{_fromlist}}, @{{star}}).valueOf() > 0) {
            for (name in @{{module}}['$dict']) {
                if (name.charAt(0) != '_') {
                    v = @{{module}}['$dict'][name];
                    if (typeof v != "undefined") {
                        @{{locals}}[name] = v;
                    }
                }
            }
        } else {
            for (var i = 0; i < @{{_fromlist}}.__array.length; i++) {
                name = @{{_fromlist}}.__array[i]['__s'];
                v = @{{module}}['$dict'][name]
                if (typeof v == "undefined") {
                    @{{raise}}($new(@{{ImportError}}, B$str("cannot import name " + name)));
                }
                @{{locals}}[name] = v;
            }
        }
    }
    """)
    return module

def _start_main(name):
    name = JS("typeof @{{name}} == 'string' ? $new(@{{str}}, @{{name}}) : @{{name}}")
    imp.load_module('__main__', *imp.find_module(name))



__all__ = []
for name in [
    'ArithmeticError',
    'AssertionError',
    'AttributeError',
    'BaseException',
    'BufferError',
    'BytesWarning',
    'DeprecationWarning',
    'EOFError',
    'Ellipsis',
    'EnvironmentError',
    'Exception',
    'False',
    'FloatingPointError',
    'FutureWarning',
    'GeneratorExit',
    'IOError',
    'ImportError',
    'ImportWarning',
    'IndentationError',
    'IndexError',
    'KeyError',
    'KeyboardInterrupt',
    'LookupError',
    'MemoryError',
    'NameError',
    'None',
    'NotImplemented',
    'NotImplementedError',
    'OSError',
    'OverflowError',
    'PendingDeprecationWarning',
    'ReferenceError',
    'RuntimeError',
    'RuntimeWarning',
    'StandardError',
    'StopIteration',
    'SyntaxError',
    'SyntaxWarning',
    'SystemError',
    'SystemExit',
    'TabError',
    'True',
    'TypeError',
    'UnboundLocalError',
    'UnicodeDecodeError',
    'UnicodeEncodeError',
    'UnicodeError',
    'UnicodeTranslateError',
    'UnicodeWarning',
    'UserWarning',
    'ValueError',
    'Warning',
    'ZeroDivisionError',
    '_',
    '__builtins__',
    '__debug__',
    '__doc__',
    '__import__',
    '__name__',
    '__package__',
    'abs',
    'all',
    'any',
    'apply',
    'basestring',
    'bin',
    'bool',
    'buffer',
    'bytearray',
    'bytes',
    'callable',
    'chr',
    'classmethod',
    'cmp',
    'coerce',
    'compile',
    'complex',
    'copyright',
    'credits',
    'delattr',
    'dict',
    'dir',
    'divmod',
    'enumerate',
    'eval',
    'execfile',
    'exit',
    'file',
    'filter',
    'float',
    'format',
    'frozenset',
    'getattr',
    'globals',
    'hasattr',
    'hash',
    'help',
    'hex',
    'id',
    'input',
    'int',
    'intern',
    'isinstance',
    'issubclass',
    'iter',
    'len',
    'license',
    'list',
    'locals',
    'long',
    'map',
    'max',
    'min',
    'next',
    'object',
    'oct',
    'open',
    'ord',
    'pow',
    'print',
    'property',
    'quit',
    'range',
    'raw_input',
    'reduce',
    'reload',
    'repr',
    'reversed',
    'round',
    'set',
    'setattr',
    'slice',
    'sorted',
    'staticmethod',
    'str',
    'sum',
    'super',
    'tuple',
    'type',
    'unichr',
    'unicode',
    'vars',
    'xrange',
    'zip',
].__iter__():
    if JS("""typeof ${0,g}$[@{{name}}.__s] != "undefined" """):
        __all__.append(name)
__nondynamic_modules__ = {}

debugger()
JS("""
@{{sys}} = $pyjs.loaded_modules['sys'];
@{{imp}} = $pyjs.loaded_modules['imp'];
@{{imp}}['$dict']['sys'] = @{{sys}};
@{{sys}}['$dict']['modules'] = $new(@{{dict}}, [[@{{__name__}}, ${0, module}$]]);
@{{_builtin_object_}} = {};
for (var i = 0; i <  @{{__all__}}.__array.length; i++) {
    var name = @{{__all__}}.__array[i].valueOf();
    @{{_builtin_object_}}[name] = ${0,globals}$[name];
}
""")
non_reloadable_module_names = ['__builtin__', 'sys', 'exceptions', '__main__']
# Import the rest of the builtin modules
for name in ['sys', 'imp', 'dynamic']:
    # Get the module from $pyjs.loaded_modules
    # and create / update the global for this module ${0,g}$[@{{name}}.valueOf()]
    # and store this in sys.module
    JS("""\
    @{{m}} = $pyjs.loaded_modules[@{{name}}.valueOf()];
    @{{m}}.$module_init();
    """)
    sys.modules[name] = m

import sys # needed for debug option
import imp
import dynamic # needed for ___import___

