/*
* The MIT License (MIT)
*
* Copyright (c) Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/

#include "absvalue.h"
#include "knownmethods.h"

AnyValue Any;
UndefinedValue Undefined;
IntegerValue Integer;
InternIntegerValue InternInteger;
BigIntegerValue BigInteger;
FloatValue Float;
BoolValue Bool;
ListValue List;
TupleValue Tuple;
SetValue Set;
FrozenSetValue FrozenSet;
StringValue String;
BytesValue Bytes;
DictValue Dict;
NoneValue None;
FunctionValue Function;
SliceValue Slice;
ComplexValue Complex;
IterableValue Iterable;
BuiltinValue Builtin;
ModuleValue Module;
TypeValue Type;
ByteArrayValue ByteArray;
MethodValue Method;
CodeObjectValue CodeObject;
EnumeratorValue Enumerator;
FileValue File;


AbstractSource::AbstractSource(py_opindex producer) {
    Sources = shared_ptr<AbstractSources>(new AbstractSources());
    Sources->Sources.insert(this);
    _producer = producer;
}

AbstractValue* AbstractValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    return &Any;
}

AbstractValue* AbstractValue::unary(AbstractSource* selfSources, int op) {
    return &Any;
}

AbstractValue* AbstractValue::compare(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    if (isKnownType(kind()) && isKnownType(other.Value->kind()) && kind() == other.Value->kind()) {
        // We know all of the known types don't have fancy rich comparison
        // operations and will return true/false.  This is in contrast to
        // user defined types which can override the rich comparison methods
        // and return values which are not bools.
        return &Bool;
    }

    return &Any;
}

AbstractValue* AbstractValue::mergeWith(AbstractValue*other) {
    if (this == other) {
        return this;
    }
    return &Any;
}

PyTypeObject *AbstractValue::pythonType() {
    return GetPyType(this->kind());
}

bool AbstractValue::known() {
    return isKnownType(this->kind());
}

AbstractSource* AbstractSource::combine(AbstractSource* one, AbstractSource* two) {
    if (one == two) {
        return one;
    }
    if (one != nullptr) {
        if (two != nullptr) {
            if (one->Sources.get() == two->Sources.get()) {
                return one;
            }

            // link the sources...
            if (one->Sources->Sources.size() > two->Sources->Sources.size()) {
                for (auto source : two->Sources->Sources) {
                    one->Sources->Sources.insert(source);
                    if (source != two) {
                        source->Sources = one->Sources;
                    }
                }
                two->Sources = one->Sources;
                return one;
            }
            else {
                for (auto source : one->Sources->Sources) {
                    two->Sources->Sources.insert(source);
                    if (source != one) {
                        source->Sources = two->Sources;
                    }
                }
                one->Sources = two->Sources;
                return two;
            }
        }
        else {
            // merging with an unknown source...
            return one;
        }
    }
    else if (two != nullptr) {
        // merging with an unknown source...
        return two;
    }
    return nullptr;
}

// BoolValue methods
AbstractValueKind BoolValue::kind() {
    return AVK_Bool;
}

AbstractValue* BoolValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_AND:
            case BINARY_OR:
            case BINARY_XOR:
            case INPLACE_AND:
            case INPLACE_OR:
            case INPLACE_XOR:
                return this;
            case BINARY_TRUE_DIVIDE:
            case INPLACE_TRUE_DIVIDE:
            {
                return &Float;
            }
            case BINARY_ADD:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_LSHIFT:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_RSHIFT:
            case BINARY_SUBTRACT:
            case INPLACE_ADD:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_LSHIFT:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_RSHIFT:
            case INPLACE_SUBTRACT:
                return &Integer;
        }
    }
    else if (other_kind == AVK_Bytes) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &Bytes;
        }
    }
    else if (other_kind == AVK_Complex) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return &Complex;
        }
    }
    else if (other_kind == AVK_Float) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
            {
                return &Float;
            }
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_MODULO:
            case INPLACE_MODULO:
                return this;
            case BINARY_TRUE_DIVIDE:
            case INPLACE_TRUE_DIVIDE:
            {
                return &Float;
            }
            case BINARY_ADD:
            case BINARY_AND:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_LSHIFT:
            case BINARY_MULTIPLY:
            case BINARY_OR:
            case BINARY_POWER:
            case BINARY_RSHIFT:
            case BINARY_SUBTRACT:
            case BINARY_XOR:
            case INPLACE_ADD:
            case INPLACE_AND:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_LSHIFT:
            case INPLACE_MULTIPLY:
            case INPLACE_OR:
            case INPLACE_POWER:
            case INPLACE_RSHIFT:
            case INPLACE_SUBTRACT:
            case INPLACE_XOR:
                return &Integer;
        }
    }
    else if (other_kind == AVK_List) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &List;
        }
    }
    else if (other_kind == AVK_String) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &String;
        }
    }
    else if (other_kind == AVK_Tuple) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &Tuple;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* BoolValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return this;
        case UNARY_INVERT:
        case UNARY_NEGATIVE:
        case UNARY_POSITIVE:
            return &Integer;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* BoolValue::describe() {
    return "bool";
}

// BytesValue methods
AbstractValueKind BytesValue::kind() {
    return AVK_Bytes;
}

AbstractValue* BytesValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_Bytes) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MODULO:
            case INPLACE_ADD:
            case INPLACE_MODULO:
                return this;
        }
    }
    else if (other_kind == AVK_Dict) {
        switch (op) {
            case BINARY_MODULO:
            case INPLACE_MODULO:
                return this;
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
            case BINARY_SUBSCR:
                return &Integer;
        }
    }
    else if (other_kind == AVK_List) {
        switch (op) {
            case BINARY_MODULO:
            case INPLACE_MODULO:
                return this;
        }
    }
    else if (other_kind == AVK_Slice) {
        switch (op) {
            case BINARY_SUBSCR:
                return this;
        }
    }
    else if (other_kind == AVK_Tuple) {
        switch (op) {
            case BINARY_MODULO:
            case INPLACE_MODULO:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* BytesValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* BytesValue::describe() {
    return "bytes";
}

AbstractValueKind BytesValue::resolveMethod(const char *name) {
    for (auto const &b: bytesMethodReturnTypes){
        if (strcmp(name, b.first) == 0)
            return b.second;
    }
    return AVK_Any;
}

// ComplexValue methods
AbstractValueKind ComplexValue::kind() {
    return AVK_Complex;
}

AbstractValue* ComplexValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return this;
        }
    }
    else if (other_kind == AVK_Complex) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return this;
        }
    }
    else if (other_kind == AVK_Float) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return this;
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* ComplexValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
        case UNARY_NEGATIVE:
        case UNARY_POSITIVE:
            return this;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* ComplexValue::describe() {
    return "complex";
}

// IntegerValue methods
AbstractValueKind IntegerValue::kind() {
    return AVK_Integer;
}

AbstractValue* IntegerValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    return IntegerValue::binary(op, other);
}

AbstractValue* IntegerValue::binary(int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_TRUE_DIVIDE:
            case INPLACE_TRUE_DIVIDE:
            {
                return &Float;
            }
            case BINARY_ADD:
            case BINARY_AND:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_LSHIFT:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_OR:
            case BINARY_POWER:
            case BINARY_RSHIFT:
            case BINARY_SUBTRACT:
            case BINARY_XOR:
            case INPLACE_ADD:
            case INPLACE_AND:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_LSHIFT:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_OR:
            case INPLACE_POWER:
            case INPLACE_RSHIFT:
            case INPLACE_SUBTRACT:
            case INPLACE_XOR:
				return &Integer;
        }
    }
    else if (other_kind == AVK_Bytes) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &Bytes;
        }
    }
    else if (other_kind == AVK_Complex) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return &Complex;
        }
    }
    else if (other_kind == AVK_Float) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
            {
                return &Float;
            }
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_TRUE_DIVIDE:
            case INPLACE_TRUE_DIVIDE:
            {
                return &Float;
            }
            case BINARY_ADD:
            case BINARY_AND:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_LSHIFT:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_OR:
            case BINARY_POWER:
            case BINARY_RSHIFT:
            case BINARY_SUBTRACT:
            case BINARY_XOR:
            case INPLACE_ADD:
            case INPLACE_AND:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_LSHIFT:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_OR:
            case INPLACE_POWER:
            case INPLACE_RSHIFT:
            case INPLACE_SUBTRACT:
            case INPLACE_XOR:
                return &Integer;
        }
    }
    else if (other_kind == AVK_List) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &List;
        }
    }
    else if (other_kind == AVK_String) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &String;
        }
    }
    else if (other_kind == AVK_Tuple) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return &Tuple;
        }
    }
    return &Any;
}

AbstractValue* IntegerValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
        case UNARY_INVERT:
        case UNARY_NEGATIVE:
        case UNARY_POSITIVE:
            return this;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* IntegerValue::describe() {
    return "int";
}

AbstractValueKind IntegerValue::resolveMethod(const char *name) {
    for (auto const &b: intMethodReturnTypes){
        if (strcmp(name, b.first) == 0)
            return b.second;
    }
    return AVK_Any;
}

// StringValue methods
AbstractValueKind StringValue::kind() {
    return AVK_String;
}

AbstractValue* StringValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    // String interpolation always returns a `str` (when successful).
    if (op == BINARY_MODULO || op == INPLACE_MODULO) {
        return this;
    }

    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_MULTIPLY:
            case BINARY_SUBSCR:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_Slice) {
        switch (op) {
            case BINARY_SUBSCR:
                return this;
        }
    }
    else if (other_kind == AVK_String) {
        switch (op) {
            case BINARY_ADD:
            case INPLACE_ADD:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* StringValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* StringValue::describe() {
    return "str";
}

AbstractValueKind StringValue::resolveMethod(const char *name) {
    for (auto const &b: stringMethodReturnTypes){
        if (strcmp(name, b.first) == 0)
            return b.second;
    }
    return AVK_Any;
}

// FloatValue methods
AbstractValueKind FloatValue::kind() {
    return AVK_Float;
}

AbstractValue* FloatValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    return FloatValue::binary(op, other);
}

AbstractValue* FloatValue::binary(int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return &Float;
        }
    }
    else if (other_kind == AVK_Complex) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return &Complex;
        }
    }
    else if (other_kind == AVK_Float) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return &Float;
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_ADD:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_MODULO:
            case BINARY_MULTIPLY:
            case BINARY_POWER:
            case BINARY_SUBTRACT:
            case BINARY_TRUE_DIVIDE:
            case INPLACE_ADD:
            case INPLACE_FLOOR_DIVIDE:
            case INPLACE_MODULO:
            case INPLACE_MULTIPLY:
            case INPLACE_POWER:
            case INPLACE_SUBTRACT:
            case INPLACE_TRUE_DIVIDE:
                return &Float;
        }
    }
    return &Any;
}

AbstractValue* FloatValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
        case UNARY_NEGATIVE:
        case UNARY_POSITIVE:
            return this;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* FloatValue::describe() {
    return "float";
}

// TupleValue methods
AbstractValueKind TupleValue::kind() {
    return AVK_Tuple;
}

AbstractValue* TupleValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_Slice) {
        switch (op) {
            case BINARY_SUBSCR:
                return this;
        }
    }
    else if (other_kind == AVK_Tuple) {
        switch (op) {
            case BINARY_ADD:
            case INPLACE_ADD:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* TupleValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* TupleValue::describe() {
    return "tuple";
}

// ListValue methods
AbstractValueKind ListValue::kind() {
    return AVK_List;
}

AbstractValue* ListValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Bool) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_Bytes) {
        switch (op) {
            case INPLACE_ADD:
                return this;
        }
    }
    else if (other_kind == AVK_Dict) {
        switch (op) {
            case INPLACE_ADD:
                return this;
        }
    }
    else if (other_kind == AVK_Integer) {
        switch (op) {
            case BINARY_MULTIPLY:
            case INPLACE_MULTIPLY:
                return this;
        }
    }
    else if (other_kind == AVK_List) {
        switch (op) {
            case BINARY_ADD:
            case INPLACE_ADD:
                return this;
        }
    }
    else if (other_kind == AVK_Set) {
        switch (op) {
            case INPLACE_ADD:
                return this;
        }
    }
    else if (other_kind == AVK_Slice) {
        switch (op) {
            case BINARY_SUBSCR:
                return this;
        }
    }
    else if (other_kind == AVK_String) {
        switch (op) {
            case INPLACE_ADD:
                return this;
        }
    }
    else if (other_kind == AVK_Tuple) {
        switch (op) {
            case INPLACE_ADD:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* ListValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* ListValue::describe() {
    return "list";
}


AbstractValueKind ListValue::resolveMethod(const char *name) {
    for (auto const &b: listMethodReturnTypes){
        if (strcmp(name, b.first) == 0)
            return b.second;
    }
    return AVK_Any;
}

// DictValue methods
AbstractValueKind DictValue::kind() {
    return AVK_Dict;
}

AbstractValue* DictValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* DictValue::describe() {
    return "dict";
}

AbstractValueKind DictValue::resolveMethod(const char *name) {
    for (auto const &b: dictMethodReturnTypes){
        if (strcmp(name, b.first) == 0)
            return b.second;
    }
    return AVK_Any;
}


// SetValue methods
AbstractValueKind SetValue::kind() {
    return AVK_Set;
}

AbstractValue* SetValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Set) {
        switch (op) {
            case BINARY_AND:
            case BINARY_OR:
            case BINARY_SUBTRACT:
            case BINARY_XOR:
            case INPLACE_AND:
            case INPLACE_OR:
            case INPLACE_SUBTRACT:
            case INPLACE_XOR:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* SetValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* SetValue::describe() {
    return "set";
}

// FrozenSetValue methods
AbstractValueKind FrozenSetValue::kind() {
    return AVK_FrozenSet;
}

AbstractValue* FrozenSetValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto other_kind = other.Value->kind();
    if (other_kind == AVK_Set || other_kind == AVK_FrozenSet) {
        switch (op) {
            case BINARY_AND:
            case BINARY_OR:
            case BINARY_SUBTRACT:
            case BINARY_XOR:
            case INPLACE_AND:
            case INPLACE_OR:
            case INPLACE_SUBTRACT:
            case INPLACE_XOR:
                return this;
        }
    }
    return AbstractValue::binary(selfSources, op, other);
}

AbstractValue* FrozenSetValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* FrozenSetValue::describe() {
    return "frozenset";
}

// NoneValue methods
AbstractValueKind NoneValue::kind() {
    return AVK_None;
}

AbstractValue* NoneValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* NoneValue::describe() {
    return "None";
}

// FunctionValue methods
AbstractValueKind FunctionValue::kind() {
    return AVK_Function;
}

AbstractValue* FunctionValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}

const char* FunctionValue::describe() {
    return "function";
}

// SliceValue methods
AbstractValueKind SliceValue::kind() {
    return AVK_Slice;
}
AbstractValue* SliceValue::unary(AbstractSource* selfSources, int op) {
    switch (op) {
        case UNARY_NOT:
            return &Bool;
    }
    return AbstractValue::unary(selfSources, op);
}
const char* SliceValue::describe() {
    return "slice";
}

// Iterable methods
AbstractValueKind IterableValue::kind() {
    return AVK_Iterable;
}
AbstractValue* IterableValue::unary(AbstractSource* selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}
const char* IterableValue::describe() {
    return "iterable";
}

// Builtin methods
AbstractValueKind BuiltinValue::kind() {
    return AVK_Function;
}
AbstractValue* BuiltinValue::unary(AbstractSource* selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}
const char* BuiltinValue::describe() {
    return "builtin";
}

// Builtin methods
AbstractValueKind ModuleValue::kind(){
    return AVK_Module;
}
AbstractValue *ModuleValue::unary(AbstractSource *selfSources, int op){
    return AbstractValue::unary(selfSources, op);
}
const char *ModuleValue::describe(){
    return "module";
}


// Type methods
AbstractValueKind TypeValue::kind() {
    return AVK_Type;
}

AbstractValue *TypeValue::unary(AbstractSource *selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}

const char *TypeValue::describe() {
    return "type";
}

// ByteArray methods
AbstractValueKind ByteArrayValue::kind() {
    return AVK_Bytearray;
}

AbstractValue *ByteArrayValue::unary(AbstractSource *selfSources, int op) {
    if (op == UNARY_NOT)
        return &Bool;
    return AbstractValue::unary(selfSources, op);
}

const char *ByteArrayValue::describe() {
    return "bytearray";
}

AbstractValueKind ByteArrayValue::resolveMethod(const char *name) {
    for (auto const &b: bytearrayMethodReturnTypes){
        if (strcmp(name, b.first) == 0)
            return b.second;
    }
    return AVK_Any;
}

// Method methods
AbstractValueKind MethodValue::kind() {
    return AVK_Method;
}

AbstractValue *MethodValue::unary(AbstractSource *selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}

const char *MethodValue::describe() {
    return "method";
}

/* Enumerator Value */
AbstractValueKind EnumeratorValue::kind() {
    return AVK_Enumerate;
}

AbstractValue *EnumeratorValue::unary(AbstractSource *selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}

const char *EnumeratorValue::describe() {
    return "enumerator";
}

/* File Value */
AbstractValueKind FileValue::kind() {
    return AVK_File;
}

AbstractValue *FileValue::unary(AbstractSource *selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}

const char *FileValue::describe() {
    return "file";
}

/* Code Object Value */
AbstractValueKind CodeObjectValue::kind() {
    return AVK_Code;
}

AbstractValue *CodeObjectValue::unary(AbstractSource *selfSources, int op) {
    return AbstractValue::unary(selfSources, op);
}

const char *CodeObjectValue::describe() {
    return "codeobject";
}

AbstractValueKind knownFunctionReturnType(AbstractValueWithSources source){
    // IS this a builtin?
    if (source.hasSource() && source.Sources->isBuiltin())
    {
        auto globalSource = dynamic_cast<BuiltinSource*>(source.Sources);
        for (auto const &b: builtinReturnTypes){
            if (strcmp(globalSource->getName(), b.first) == 0)
                return b.second;
        }
    }
    return AVK_Any;
}

AbstractValue* avkToAbstractValue(AbstractValueKind kind){
    switch (kind) {
        case AVK_Any:
            return &Any;
        case AVK_Undefined:
            return &Any;
        case AVK_Integer:
            return &Integer;
        case AVK_Float:
            return &Float;
        case AVK_Bool:
            return &Bool;
        case AVK_List:
            return &List;
        case AVK_Dict:
            return &Dict;
        case AVK_Tuple:
            return &Tuple;
        case AVK_Set:
            return &Set;
        case AVK_FrozenSet:
            return &FrozenSet;
        case AVK_String:
            return &String;
        case AVK_Bytes:
            return &Bytes;
        case AVK_Bytearray:
            return &ByteArray;
        case AVK_None:
            return &None;
        case AVK_Function:
            return &Function;
        case AVK_Slice:
            return &Slice;
        case AVK_Complex:
            return &Complex;
        case AVK_Iterable:
            return &Iterable;
        case AVK_Code:
            return &CodeObject;
        case AVK_Enumerate:
            return &Enumerator;
        case AVK_File:
            return &File;
        case AVK_Type:
            return &Type;
        case AVK_Module:
            return &Module;

        default:
            return &Any;
    }
}

AbstractValueKind GetAbstractType(PyTypeObject* type) {
    if (type == nullptr) {
        return AVK_Any;
    } else if (type == &PyLong_Type) {
        return AVK_Integer;
    }
    else if (type == &PyFloat_Type) {
        return AVK_Float;
    }
    else if (type == &PyDict_Type) {
        return AVK_Dict;
    }
    else if (type == &PyTuple_Type) {
        return AVK_Tuple;
    }
    else if (type == &PyList_Type) {
        return AVK_List;
    }
    else if (type == &PyBool_Type) {
        return AVK_Bool;
    }
    else if (type == &PyUnicode_Type) {
        return AVK_String;
    }
    else if (type == &PyBytes_Type) {
        return AVK_Bytes;
    }
    else if (type == &PySet_Type) {
        return AVK_Set;
    }
    else if (type == &PyFrozenSet_Type) {
        return AVK_FrozenSet;
    }
    else if (type == &_PyNone_Type) {
        return AVK_None;
    }
    else if (type == &PyFunction_Type || type == &PyCFunction_Type) {
        return AVK_Function;
    }
    else if (type == &PySlice_Type) {
        return AVK_Slice;
    }
    else if (type == &PyComplex_Type) {
        return AVK_Complex;
    }
    else if (type == &PyType_Type) {
        return AVK_Type;
    }
    else if (type == &PyEnum_Type) {
        return AVK_Enumerate;
    }
    else if (type == &PyCode_Type) {
        return AVK_Code;
    }
    return AVK_Any;
}

PyTypeObject* GetPyType(AbstractValueKind type) {
    switch (type) {
        case AVK_Any: return &PyType_Type;
        case AVK_Integer: return &PyLong_Type;
        case AVK_BigInteger: return &PyLong_Type;
        case AVK_Float: return &PyFloat_Type;
        case AVK_Dict: return &PyDict_Type;
        case AVK_Tuple: return &PyTuple_Type;
        case AVK_List: return &PyList_Type;
        case AVK_Bool: return &PyBool_Type;
        case AVK_String: return &PyUnicode_Type;
        case AVK_Bytes: return &PyBytes_Type;
        case AVK_Set: return &PySet_Type;
        case AVK_FrozenSet: return &PyFrozenSet_Type;
        case AVK_None: return &_PyNone_Type;
        case AVK_Function: return &PyFunction_Type;
        case AVK_Slice: return &PySlice_Type;
        case AVK_Complex: return &PyComplex_Type;
        case AVK_Type: return &PyType_Type;
        case AVK_Enumerate: return &PyEnum_Type;
        case AVK_Code: return &PyCode_Type;
        case AVK_Bytearray: return &PyByteArray_Type;
        case AVK_Module: return &PyModule_Type;
        case AVK_Method: return &PyMethod_Type;
        // TODO : resolve missing AVK_File and AVK_Iterable
        default:
        #ifdef DEBUG
            printf("Warning: Missing GetPyType for %d", type);
        #endif
            return nullptr;
    }
}

PyTypeObject* VolatileValue::pythonType() {
    return this->_type;
}

AbstractValueKind VolatileValue::kind() {
    return GetAbstractType(this->_type);
}

AbstractValue* VolatileValue::binary(AbstractSource* selfSources, int op, AbstractValueWithSources& other) {
    auto my_kind = GetAbstractType(_type);

    switch(my_kind){
        case AVK_Float:
            return FloatValue::binary(op, other);
        case AVK_Integer:
            return IntegerValue::binary(op, other);
    }
    return AbstractValue::binary(selfSources, op, other);
}