/**
 * This is the C++ adaptation and derivative work of Myia (https://github.com/mila-iqia/myia/).
 *
 * Copyright 2019 Huawei Technologies Co., Ltd
 *
 * 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.
 */

#include "pipeline/static_analysis/abstract_value.h"

#include <algorithm>

#include "utils/symbolic.h"
#include "pipeline/static_analysis/static_analysis.h"
#include "pipeline/static_analysis/utils.h"

namespace mindspore {
namespace abstract {
bool AbstractBase::operator==(const AbstractBase &other) const {
  if (tid() != other.tid()) {
    return false;
  }
  if (value_ == nullptr || other.value_ == nullptr) {
    MS_LOG(EXCEPTION) << "If value_ is nullptr, AbstractBase::operator== should not be called. this: "
                      << this->ToString() << ", other: " << other.ToString();
  }

  bool value_equal = *value_ == *other.value_;
  bool type_equal = *type_ == *other.type_;
  bool shape_equal = *shape_ == *other.shape_;
  return value_equal && type_equal && shape_equal;
}

ValuePtr AbstractBase::BuildValue() const {
  if (value_ == nullptr) {
    return RealBuildValue();
  }
  return value_;
}

AbstractBasePtr AbstractBase::Broaden() const {
  AbstractBasePtr clone = Clone();
  clone->set_value(kAnyValue);
  return clone;
}

std::string AbstractBase::ToString() const {
  std::ostringstream buffer;
  std::string value = std::string("value is null");
  if (value_ != nullptr) {
    value = value_->ToString();
  }
  MS_EXCEPTION_IF_NULL(type_);
  MS_EXCEPTION_IF_NULL(shape_);
  buffer << type_name() << "("
         << "Type: " << type_->ToString() << " Value: " << value << " Shape: " << shape_->ToString() << ")";
  return buffer.str();
}

AbstractBasePtr AbstractScalar::Broaden() const {
  AbstractBasePtr clone = Clone();
  MS_EXCEPTION_IF_NULL(clone);
  auto value_track = clone->GetValueTrack();
  MS_EXCEPTION_IF_NULL(value_track);
  if (value_track->isa<SymbolicKeyInstance>()) {
    return clone;
  }
  return AbstractBase::Broaden();
}

AbstractBasePtr AbstractScalar::Join(const AbstractBasePtr &other) {
  MS_EXCEPTION_IF_NULL(other);
  if (*this == *other) {
    return shared_from_base<AbstractBase>();
  }
  auto value_self = GetValueTrack();
  MS_EXCEPTION_IF_NULL(value_self);
  ValuePtr res_value = ValueJoin(value_self, other->GetValueTrack());
  TypePtr res_type = TypeJoin(GetTypeTrack(), other->GetTypeTrack());
  if (res_value == value_self) {
    return shared_from_base<AbstractBase>();
  }
  return std::make_shared<AbstractScalar>(res_value, res_type);
}

AbstractBasePtr AbstractType::Clone() const {
  ValuePtr value_self = GetValueTrack();
  if (value_self == nullptr || !value_self->isa<Type>()) {
    return nullptr;
  }
  TypePtr type_self = value_self->cast<TypePtr>();
  return std::make_shared<AbstractType>(type_self->Clone());
}

bool AbstractType::operator==(const AbstractBase &other) const {
  if (tid() != other.tid()) {
    return false;
  }
  // Have to compare TypePtr with value;
  ValuePtr value_self = GetValueTrack();
  ValuePtr value_other = other.GetValueTrack();
  if (value_self == nullptr || value_other == nullptr) {
    MS_LOG(EXCEPTION) << "AbstractType value should not be nullptr. this: " << this->ToString()
                      << ", other: " << other.ToString();
  }
  if (!value_self->isa<Type>() || !value_other->isa<Type>()) {
    return false;
  }
  TypePtr type_self = value_self->cast<TypePtr>();
  TypePtr type_other = value_other->cast<TypePtr>();
  bool value_equal = *type_self == *type_other;
  return value_equal;
}

std::string AbstractType::ToString() const {
  std::ostringstream buffer;
  ValuePtr value_self = GetValueTrack();
  if (value_self == nullptr) {
    buffer << "AbstractType value: nullptr";
    return buffer.str();
  }
  if (!value_self->isa<Type>()) {
    buffer << type_name() << "(Value: nullptr)";
    return buffer.str();
  }
  TypePtr type_self = value_self->cast<TypePtr>();
  MS_EXCEPTION_IF_NULL(type_self);
  buffer << type_name() << "("
         << "Value: " << type_self->ToString() << ")";
  return buffer.str();
}

std::string AbstractError::ToString() const {
  std::ostringstream buffer;
  auto value_track = GetValueTrack();
  MS_EXCEPTION_IF_NULL(value_track);
  buffer << type_name() << "("
         << "Value: " << value_track->ToString() << ", Node: " << node_->DebugString() << ")";
  return buffer.str();
}

AbstractBasePtr AbstractFunction::Join(const AbstractBasePtr &other) {
  MS_EXCEPTION_IF_NULL(other);
  auto other_func = dyn_cast<AbstractFunction>(other);
  if (other_func == nullptr) {
    MS_LOG(EXCEPTION) << "Join failed as type mismatch, this: " << ToString() << ", other: " << other->ToString();
  }
  return Join(other_func);
}

bool AbstractFunction::operator==(const AbstractBase &other) const {
  if (!other.isa<AbstractFunction>()) {
    return false;
  }
  const auto &other_func = static_cast<const AbstractFunction &>(other);
  bool value_equal = (*this == other_func);
  return value_equal;
}

const AbstractBasePtr AbstractSequeue::operator[](const std::size_t &dim) const {
  if (dim >= size()) {
    MS_LOG(EXCEPTION) << "Index [" << dim << "] Out of the size [" << size() << "] of the list.";
  }
  return elements_[dim];
}

std::string AbstractSequeue::ToString() const {
  std::ostringstream buffer;
  int i = 0;
  for (const auto &ele : elements_) {
    MS_EXCEPTION_IF_NULL(ele);
    buffer << "element[" << i << "]: " << ele->ToString() << ",";
    i++;
  }
  return buffer.str();
}

TypePtrList AbstractSequeue::ElementsType() const {
  TypePtrList element_type_list;
  for (const auto &ele : elements_) {
    MS_EXCEPTION_IF_NULL(ele);
    TypePtr element_type = ele->BuildType();
    element_type_list.push_back(element_type);
  }
  return element_type_list;
}

BaseShapePtrList AbstractSequeue::ElementsShape() const {
  BaseShapePtrList element_shape_list;
  for (const auto &ele : elements_) {
    MS_EXCEPTION_IF_NULL(ele);
    BaseShapePtr element_shape = ele->BuildShape();
    element_shape_list.push_back(element_shape);
  }
  return element_shape_list;
}

AbstractBasePtrList AbstractSequeue::ElementsClone() const {
  AbstractBasePtrList ele_list;
  for (const auto &ele : elements_) {
    MS_EXCEPTION_IF_NULL(ele);
    AbstractBasePtr clone = ele->Clone();
    ele_list.push_back(clone);
  }
  return ele_list;
}

AbstractBasePtrList AbstractSequeue::ElementsBroaden() const {
  AbstractBasePtrList ele_list;
  for (const auto &ele : elements_) {
    MS_EXCEPTION_IF_NULL(ele);
    AbstractBasePtr broadend = ele->Broaden();
    ele_list.push_back(broadend);
  }
  return ele_list;
}

template <typename T>
ValuePtr AbstractSequeue::ElementsBuildValue() const {
  std::vector<ValuePtr> element_value_list;
  for (const auto &ele : elements_) {
    ValuePtr element_value = ele->BuildValue();
    if (element_value->isa<AnyValue>()) {
      return kAnyValue;
    }
    element_value_list.push_back(element_value);
  }
  return std::make_shared<T>(element_value_list);
}
template ValuePtr AbstractSequeue::ElementsBuildValue<ValueTuple>() const;
template ValuePtr AbstractSequeue::ElementsBuildValue<ValueList>() const;

template <typename T>
AbstractBasePtr AbstractSequeue::ElementsJoin(const AbstractBasePtr &other) {
  auto other_sequeue = dyn_cast<T>(other);
  if (other_sequeue == nullptr) {
    MS_LOG(EXCEPTION) << "Join failed as type mismatch, this: " << ToString() << ", other: " << other->ToString();
  }
  auto joined_list = AbstractJoin(elements_, other_sequeue->elements_);
  bool changes = false;
  for (std::size_t i = 0; i < elements_.size(); i++) {
    if (elements_[i] != joined_list[i]) {
      changes = true;
      break;
    }
  }
  if (!changes) {
    return shared_from_base<AbstractBase>();
  }
  return std::make_shared<T>(joined_list);
}
template AbstractBasePtr AbstractSequeue::ElementsJoin<AbstractList>(const AbstractBasePtr &);
template AbstractBasePtr AbstractSequeue::ElementsJoin<AbstractTuple>(const AbstractBasePtr &);

std::size_t AbstractSequeue::hash() const {
  std::size_t hash_sum = hash_combine(tid(), std::hash<size_t>{}(elements_.size()));
  // Hashing all elements is costly, so only take at most 4 elements into account based on
  // some experiments.
  for (size_t i = 0; (i < elements_.size()) && (i < 4); i++) {
    hash_sum = hash_combine(hash_sum, elements_[i]->hash());
  }
  return hash_sum;
}

bool AbstractTuple::operator==(const AbstractTuple &other) const {
  if (&other == this) {
    return true;
  }

  if (elements_.size() != other.elements_.size()) {
    return false;
  }
  for (size_t i = 0; i < elements_.size(); i++) {
    if (!(*(elements_[i]) == *(other.elements_[i]))) {
      return false;
    }
  }
  return true;
}

bool AbstractTuple::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }

  if (other.isa<AbstractTuple>()) {
    auto other_tuple = static_cast<const AbstractTuple *>(&other);
    return *this == *other_tuple;
  }

  return false;
}

bool AbstractList::operator==(const AbstractList &other) const {
  if (&other == this) {
    return true;
  }

  if (elements_.size() != other.elements_.size()) {
    return false;
  }
  for (size_t i = 0; i < elements_.size(); i++) {
    if (!(*(elements_[i]) == *(other.elements_[i]))) {
      return false;
    }
  }
  return true;
}

bool AbstractList::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }

  if (other.isa<AbstractList>()) {
    auto other_list = static_cast<const AbstractList *>(&other);
    return *this == *other_list;
  }
  return false;
}

TypePtr AbstractSlice::BuildType() const {
  MS_EXCEPTION_IF_NULL(start_);
  MS_EXCEPTION_IF_NULL(stop_);
  MS_EXCEPTION_IF_NULL(step_);
  TypePtr start = start_->BuildType();
  TypePtr stop = stop_->BuildType();
  TypePtr step = step_->BuildType();
  return std::make_shared<Slice>(start, stop, step);
}

bool AbstractSlice::operator==(const AbstractSlice &other) const {
  if (&other == this) {
    return true;
  }
  return (*start_ == *other.start_ && *stop_ == *other.stop_ && *step_ == *other.step_);
}

bool AbstractSlice::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }
  if (!other.isa<AbstractSlice>()) {
    return false;
  }
  auto other_slice = static_cast<const AbstractSlice *>(&other);
  return *this == *other_slice;
}

AbstractBasePtr AbstractSlice::Clone() const {
  MS_EXCEPTION_IF_NULL(start_);
  MS_EXCEPTION_IF_NULL(stop_);
  MS_EXCEPTION_IF_NULL(step_);
  AbstractBasePtr start = start_->Clone();
  AbstractBasePtr stop = stop_->Clone();
  AbstractBasePtr step = step_->Clone();
  return std::make_shared<AbstractSlice>(start, stop, step);
}

AbstractBasePtr AbstractSlice::Broaden() const {
  MS_EXCEPTION_IF_NULL(start_);
  MS_EXCEPTION_IF_NULL(stop_);
  MS_EXCEPTION_IF_NULL(step_);
  AbstractBasePtr start = start_->Broaden();
  AbstractBasePtr stop = stop_->Broaden();
  AbstractBasePtr step = step_->Broaden();
  return std::make_shared<AbstractSlice>(start, stop, step);
}

std::string AbstractSlice::ToString() const {
  std::ostringstream buffer;
  buffer << type_name() << "[";
  MS_EXCEPTION_IF_NULL(start_);
  buffer << start_->ToString() << " : ";
  MS_EXCEPTION_IF_NULL(stop_);
  buffer << stop_->ToString() << " : ";
  MS_EXCEPTION_IF_NULL(step_);
  buffer << step_->ToString();
  buffer << "]";
  return buffer.str();
}

ValuePtr AbstractSlice::RealBuildValue() const {
  MS_EXCEPTION_IF_NULL(start_);
  MS_EXCEPTION_IF_NULL(stop_);
  MS_EXCEPTION_IF_NULL(step_);
  ValuePtr start = start_->BuildValue();
  ValuePtr stop = stop_->BuildValue();
  ValuePtr step = step_->BuildValue();
  if (start->isa<AnyValue>() || stop->isa<AnyValue>() || step->isa<AnyValue>()) {
    return kAnyValue;
  }
  return std::make_shared<ValueSlice>(start, stop, step);
}

std::size_t AbstractSlice::hash() const {
  MS_EXCEPTION_IF_NULL(start_);
  MS_EXCEPTION_IF_NULL(stop_);
  MS_EXCEPTION_IF_NULL(step_);
  return hash_combine({tid(), start_->hash(), stop_->hash(), step_->hash()});
}

TypePtr AbstractTensor::BuildType() const {
  MS_EXCEPTION_IF_NULL(element_);
  TypePtr element_type = element_->BuildType();
  return std::make_shared<TensorType>(element_type);
}

BaseShapePtr AbstractTensor::BuildShape() const {
  auto shape = GetShapeTrack();
  // Guard from using set_shape(nullptr)
  if (shape == nullptr) {
    return kNoShape;
  }
  return shape;
}

AbstractBasePtr AbstractTensor::Join(const AbstractBasePtr &other) {
  auto other_tensor = dyn_cast<AbstractTensor>(other);
  if (other_tensor == nullptr) {
    MS_LOG(EXCEPTION) << "Join failed as type mismatch, this: " << ToString() << ", other: " << other->ToString();
  }
  auto element = element_->Join(other_tensor->element_);
  auto shape = ShapeJoin(this->shape(), other_tensor->shape());
  return std::make_shared<AbstractTensor>(element, shape);
}

bool AbstractTensor::operator==(const AbstractTensor &other) const {
  if (&other == this) {
    return true;
  }

  auto v1 = GetValueTrack();
  auto v2 = other.GetValueTrack();
  if (v1 == nullptr || v2 == nullptr) {
    MS_LOG(EXCEPTION) << "The value of AbstractTensor is nullptr";
  }

  bool is_value_equal = (v1 == v2);
  if (v1->isa<AnyValue>() && v2->isa<AnyValue>()) {
    is_value_equal = true;
  }
  return (*element_ == *other.element_) && (*shape() == *other.shape()) && is_value_equal;
}

bool AbstractTensor::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }

  if (other.isa<AbstractTensor>()) {
    auto other_tensor = static_cast<const AbstractTensor *>(&other);
    return *this == *other_tensor;
  } else {
    return false;
  }
}

AbstractBasePtr AbstractTensor::Clone() const {
  MS_EXCEPTION_IF_NULL(element_);
  auto clone = std::make_shared<AbstractTensor>(element_->Clone());
  ShapePtr shp = shape();
  clone->set_shape(shp->Clone());
  clone->set_value(GetValueTrack());
  return clone;
}

AbstractBasePtr AbstractTensor::Broaden() const {
  MS_EXCEPTION_IF_NULL(element_);
  auto broaden = std::make_shared<AbstractTensor>(element_->Broaden());
  auto shp = shape();
  broaden->set_shape(shp->Clone());
  broaden->set_value(kAnyValue);
  return broaden;
}

AbstractBasePtr AbstractTensor::BroadenWithShape() const {
  MS_EXCEPTION_IF_NULL(element_);
  auto broaden = std::make_shared<AbstractTensor>(element_->Broaden());
  auto shp = shape()->Clone();
  shp->Broaden();
  broaden->set_shape(shp);
  broaden->set_value(kAnyValue);
  return broaden;
}

ShapePtr AbstractTensor::shape() const {
  auto shp = dyn_cast<Shape>(GetShapeTrack());
  if (shp == nullptr) {
    MS_LOG(EXCEPTION) << "Tensor should have a shape.";
  }
  return shp;
}

std::string AbstractTensor::ToString() const {
  std::ostringstream buffer;
  BaseShapePtr shape_track = GetShapeTrack();
  MS_EXCEPTION_IF_NULL(shape_track);
  MS_EXCEPTION_IF_NULL(element_);
  auto value_track = GetValueTrack();
  MS_EXCEPTION_IF_NULL(value_track);
  buffer << type_name() << "("
         << "shape: " << shape_track->ToString() << ", element: " << element_->ToString()
         << ", value_ptr: " << value_track << ", value: " << value_track->ToString() << ")";
  return buffer.str();
}

TypePtr AbstractDictionary::BuildType() const {
  std::vector<std::pair<std::string, TypePtr>> key_values;
  for (const auto &item : key_values_) {
    MS_EXCEPTION_IF_NULL(item.second);
    TypePtr type = item.second->BuildType();
    key_values.emplace_back(item.first, type);
  }
  return std::make_shared<Dictionary>(key_values);
}

bool AbstractDictionary::operator==(const AbstractDictionary &other) const {
  if (key_values_.size() != other.key_values_.size()) {
    return false;
  }

  for (size_t index = 0; index < key_values_.size(); index++) {
    if (key_values_[index].first != other.key_values_[index].first) {
      return false;
    }
    if (!(*key_values_[index].second == *other.key_values_[index].second)) {
      return false;
    }
  }
  return true;
}

bool AbstractDictionary::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }
  if (other.isa<AbstractDictionary>()) {
    auto other_class = static_cast<const AbstractDictionary *>(&other);
    return *this == *other_class;
  }
  return false;
}

AbstractBasePtr AbstractDictionary::Clone() const {
  std::vector<AbstractAttribute> kv;
  (void)std::transform(key_values_.begin(), key_values_.end(), std::back_inserter(kv),
                       [](const AbstractAttribute &item) {
                         MS_EXCEPTION_IF_NULL(item.second);
                         return std::make_pair(item.first, item.second->Clone());
                       });
  return std::make_shared<AbstractDictionary>(kv);
}

AbstractBasePtr AbstractDictionary::Broaden() const {
  std::vector<AbstractAttribute> kv;
  (void)std::transform(key_values_.begin(), key_values_.end(), std::back_inserter(kv),
                       [](const AbstractAttribute &item) {
                         MS_EXCEPTION_IF_NULL(item.second);
                         return std::make_pair(item.first, item.second->Broaden());
                       });
  return std::make_shared<AbstractDictionary>(kv);
}

std::string AbstractDictionary::ToString() const {
  std::ostringstream buffer;
  buffer << type_name() << "{ ";
  for (const auto &kv : key_values_) {
    MS_EXCEPTION_IF_NULL(kv.second);
    buffer << "(" << kv.first << ": " << kv.second->ToString() << ") ";
  }
  buffer << "}";
  return buffer.str();
}

std::size_t AbstractDictionary::hash() const {
  std::size_t hash_sum = std::accumulate(key_values_.begin(), key_values_.end(), tid(),
                                         [](std::size_t hash_sum, const AbstractAttribute &item) {
                                           hash_sum = hash_combine(hash_sum, std::hash<std::string>()(item.first));
                                           MS_EXCEPTION_IF_NULL(item.second);
                                           hash_sum = hash_combine(hash_sum, item.second->hash());
                                           return hash_sum;
                                         });
  return hash_sum;
}

ValuePtr AbstractDictionary::RealBuildValue() const {
  std::vector<std::pair<std::string, ValuePtr>> key_values;
  for (const auto &item : key_values_) {
    MS_EXCEPTION_IF_NULL(item.second);
    auto element_value = item.second->BuildValue();
    MS_EXCEPTION_IF_NULL(element_value);
    if (element_value->isa<AnyValue>()) {
      return kAnyValue;
    }
    key_values.emplace_back(item.first, element_value);
  }
  return std::make_shared<ValueDictionary>(key_values);
}

TypePtr AbstractClass::BuildType() const {
  ClassAttrVector attributes_type;
  for (auto attr : attributes_) {
    MS_EXCEPTION_IF_NULL(attr.second);
    TypePtr type = attr.second->BuildType();
    std::pair<std::string, TypePtr> elem(attr.first, type);
    attributes_type.push_back(elem);
  }

  return std::make_shared<Class>(tag_, attributes_type, methods_);
}

bool AbstractClass::operator==(const AbstractClass &other) const {
  if (!(tag_ == other.tag_)) {
    return false;
  }
  if (attributes_.size() != other.attributes_.size()) {
    return false;
  }
  for (size_t i = 0; i < attributes_.size(); i++) {
    MS_EXCEPTION_IF_NULL(attributes_[i].second);
    MS_EXCEPTION_IF_NULL(other.attributes_[i].second);
    if (!(*attributes_[i].second == *other.attributes_[i].second)) {
      MS_LOG(DEBUG) << "attr " << attributes_[i].first << " not equal, arg1:" << attributes_[i].second->ToString()
                    << " arg2:" << other.attributes_[i].second->ToString();
      return false;
    }
  }
  // method compare;
  if (methods_.size() != other.methods_.size()) {
    return false;
  }
  for (const auto &iter : methods_) {
    auto iter_other = other.methods_.find(iter.first);
    if (iter_other == other.methods_.end()) {
      return false;
    }
    if (!(*iter.second == *iter_other->second)) {
      return false;
    }
  }
  return true;
}

bool AbstractClass::operator==(const AbstractBase &other) const {
  if (other.isa<AbstractClass>()) {
    auto other_class = static_cast<const AbstractClass *>(&other);
    return *this == *other_class;
  }
  return false;
}

AbstractBasePtr AbstractClass::GetAttribute(const std::string &name) {
  auto it = std::find_if(attributes_.begin(), attributes_.end(),
                         [name](const AbstractAttribute &pair) -> bool { return pair.first == name; });
  if (it != attributes_.end()) {
    return it->second;
  }
  return nullptr;
}

ValuePtr AbstractClass::GetMethod(const std::string &name) {
  auto method_pair = methods_.find(name);
  if (method_pair != methods_.end()) {
    return method_pair->second;
  }
  return kAnyValue;
}

AbstractBasePtr AbstractClass::Clone() const {
  std::vector<AbstractAttribute> attributes_clone;
  for (auto attr : attributes_) {
    MS_EXCEPTION_IF_NULL(attr.second);
    AbstractBasePtr clone = attr.second->Clone();
    AbstractAttribute elem(attr.first, clone);
    attributes_clone.push_back(elem);
  }
  return std::make_shared<AbstractClass>(tag_, attributes_clone, methods_);
}

AbstractBasePtr AbstractClass::Broaden() const {
  std::vector<AbstractAttribute> attributes_clone;
  for (auto attr : attributes_) {
    MS_EXCEPTION_IF_NULL(attr.second);
    AbstractBasePtr clone = attr.second->Broaden();
    AbstractAttribute elem(attr.first, clone);
    attributes_clone.push_back(elem);
  }
  return std::make_shared<AbstractClass>(tag_, attributes_clone, methods_);
}

std::string AbstractClass::ToString() const {
  std::ostringstream buffer;
  buffer << type_name() << "(tag: " << tag_ << ") attrs:(";
  bool append_comma = false;
  for (const auto &attr : attributes_) {
    if (append_comma) {
      buffer << ", ";
    } else {
      append_comma = true;
    }
    MS_EXCEPTION_IF_NULL(attr.second);
    buffer << attr.first << ":" << attr.second->ToString();
  }
  buffer << ") method:(";
  append_comma = false;
  for (const auto &iter : methods_) {
    if (append_comma) {
      buffer << ", ";
    } else {
      append_comma = true;
    }
    MS_EXCEPTION_IF_NULL(iter.second);
    buffer << iter.first << ":" << iter.second->ToString();
  }
  buffer << ")";
  return buffer.str();
}

std::size_t AbstractClass::hash() const {
  std::size_t hash_sum = std::accumulate(attributes_.begin(), attributes_.end(), hash_combine(tid(), tag_.hash()),
                                         [](std::size_t hash_sum, const AbstractAttribute &item) {
                                           MS_EXCEPTION_IF_NULL(item.second);
                                           return hash_combine(hash_sum, item.second->hash());
                                         });

  return hash_sum;
}

ValuePtr AbstractClass::RealBuildValue() const {
  auto cls = BuildType()->cast<ClassPtr>();
  std::unordered_map<std::string, ValuePtr> attributes_value_map;
  for (const auto &attr : attributes_) {
    MS_EXCEPTION_IF_NULL(attr.second);
    ValuePtr _value = attr.second->BuildValue();
    if (_value->isa<AnyValue>()) {
      return kAnyValue;
    }
    attributes_value_map[attr.first] = _value;
  }
  cls->set_value(attributes_value_map);
  return cls;
}

TypePtr AbstractJTagged::BuildType() const {
  MS_EXCEPTION_IF_NULL(element_);
  TypePtr subtype = element_->BuildType();
  return std::make_shared<JTagged>(subtype);
}

AbstractBasePtr AbstractJTagged::Join(const AbstractBasePtr &other) {
  auto other_jtagged = dyn_cast<AbstractJTagged>(other);
  if (other_jtagged == nullptr) {
    MS_LOG(EXCEPTION) << "Join failed as type mismatch, this: " << ToString() << ", other: " << other->ToString();
  }
  auto joined_elem = element_->Join(other_jtagged->element_);
  return std::make_shared<AbstractJTagged>(joined_elem);
}

bool AbstractJTagged::operator==(const AbstractJTagged &other) const {
  MS_EXCEPTION_IF_NULL(element_);
  MS_EXCEPTION_IF_NULL(other.element_);
  return (*element_ == *other.element_);
}

bool AbstractJTagged::operator==(const AbstractBase &other) const {
  if (other.isa<AbstractJTagged>()) {
    auto other_jtagged = static_cast<const AbstractJTagged *>(&other);
    return *this == *other_jtagged;
  }
  return false;
}

std::string AbstractJTagged::ToString() const {
  std::ostringstream buffer;
  MS_EXCEPTION_IF_NULL(element_);
  buffer << type_name() << "("
         << "element: " << element_->ToString() << ")";
  return buffer.str();
}

TypePtr AbstractRef::BuildType() const {
  TypePtr subtype = ref_->BuildType();
  TypePtr subtype_origin = ref_origin_->BuildType();
  return std::make_shared<RefType>(subtype, subtype_origin);
}

bool AbstractRef::operator==(const AbstractRef &other) const {
  return (*ref_ == *other.ref_) && (*ref_key_ == *other.ref_key_) && (*ref_origin_ == *other.ref_origin_);
}

bool AbstractRef::operator==(const AbstractBase &other) const {
  if (other.isa<AbstractRef>()) {
    auto other_conf = static_cast<const AbstractRef *>(&other);
    return *this == *other_conf;
  }
  return false;
}

std::string AbstractRef::ToString() const {
  std::ostringstream buffer;
  buffer << type_name() << "("
         << "key: " << ref_key_->ToString() << "ref_value: " << ref_->ToString()
         << "origin_value: " << ref_origin_->ToString();
  auto value = GetValueTrack();
  if (value) {
    buffer << ", value: " << value->ToString();
  }
  buffer << ")";
  return buffer.str();
}

bool AbstractNone::operator==(const AbstractNone &) const { return true; }

bool AbstractNone::operator==(const AbstractBase &other) const {
  if (other.isa<AbstractNone>()) {
    auto other_none = static_cast<const AbstractNone *>(&other);
    return *this == *other_none;
  }
  return false;
}

std::string AbstractNone::ToString() const {
  std::ostringstream buffer;
  buffer << type_name() << "(Value: None)";
  return buffer.str();
}

ValuePtr AbstractNone::RealBuildValue() const { return kNone; }

bool AbstractRefKey::operator==(const AbstractRefKey &other) const {
  ValuePtr value_self = GetValueTrack();
  ValuePtr value_other = other.GetValueTrack();
  if (value_self != nullptr && value_other != nullptr) {
    if (value_self->isa<AnyValue>() && value_other->isa<AnyValue>()) {
      return true;
    }
    if (!value_self->isa<RefKey>() || !value_other->isa<RefKey>()) {
      return false;
    }
    RefKeyPtr type_self = value_self->cast<RefKeyPtr>();
    RefKeyPtr type_other = value_other->cast<RefKeyPtr>();
    return *type_self == *type_other;
  } else if (value_self != nullptr || value_other != nullptr) {
    return false;
  }
  return true;
}

bool AbstractRefKey::operator==(const AbstractBase &other) const {
  if (other.isa<AbstractRefKey>()) {
    auto other_confkey = static_cast<const AbstractRefKey *>(&other);
    return *this == *other_confkey;
  } else {
    return false;
  }
}

std::string AbstractRefKey::ToString() const {
  std::ostringstream buffer;
  buffer << type_name();
  auto value = GetValueTrack();
  if (value) {
    buffer << "(value: " << value->ToString() << ")";
  }
  return buffer.str();
}

bool AbstractNull::operator==(const AbstractNull &) const { return true; }

bool AbstractNull::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }
  if (other.isa<AbstractNull>()) {
    auto other_none = static_cast<const AbstractNull *>(&other);
    return *this == *other_none;
  } else {
    return false;
  }
}

std::string AbstractNull::ToString() const {
  std::ostringstream buffer;
  buffer << type_name() << "("
         << "Value: "
         << "Null"
         << ")";
  return buffer.str();
}

TypePtr AbstractKeywordArg::BuildType() const {
  MS_EXCEPTION_IF_NULL(arg_value_);
  TypePtr type = arg_value_->BuildType();
  return std::make_shared<Keyword>(arg_name_, type);
}

AbstractBasePtr AbstractKeywordArg::Clone() const {
  MS_EXCEPTION_IF_NULL(arg_value_);
  return std::make_shared<AbstractKeywordArg>(arg_name_, arg_value_->Clone());
}

AbstractBasePtr AbstractKeywordArg::Broaden() const {
  MS_EXCEPTION_IF_NULL(arg_value_);
  return std::make_shared<AbstractKeywordArg>(arg_name_, arg_value_->Broaden());
}

std::size_t AbstractKeywordArg::hash() const {
  MS_EXCEPTION_IF_NULL(arg_value_);
  return hash_combine({tid(), std::hash<std::string>{}(arg_name_), arg_value_->hash()});
}

std::string AbstractKeywordArg::ToString() const {
  std::ostringstream buffer;
  MS_EXCEPTION_IF_NULL(arg_value_);
  buffer << type_name() << "(";
  buffer << "key : " << arg_name_;
  buffer << "value : " << arg_value_->ToString();
  buffer << ")";
  return buffer.str();
}

bool AbstractKeywordArg::operator==(const AbstractBase &other) const {
  if (&other == this) {
    return true;
  }

  if (other.isa<AbstractKeywordArg>()) {
    auto other_tuple = static_cast<const AbstractKeywordArg *>(&other);
    return *this == *other_tuple;
  }
  return false;
}

bool AbstractKeywordArg::operator==(const AbstractKeywordArg &other) const {
  if (&other == this) {
    return true;
  }
  MS_EXCEPTION_IF_NULL(arg_value_);
  MS_EXCEPTION_IF_NULL(other.arg_value_);
  return other.arg_name_ == arg_name_ && *other.arg_value_ == *arg_value_;
}

ValuePtr AbstractKeywordArg::RealBuildValue() const {
  MS_EXCEPTION_IF_NULL(arg_value_);
  ValuePtr value = arg_value_->BuildValue();
  MS_EXCEPTION_IF_NULL(value);
  if (value->isa<AnyValue>()) {
    return kAnyValue;
  }
  return std::make_shared<KeywordArg>(arg_name_, value);
}

std::size_t AbstractBasePtrListHash(const AbstractBasePtrList &args_spec_list) {
  std::size_t hash_value = 0;
  // Hashing all elements is costly, so only take at most 4 elements into account based on
  // some experiments.
  for (size_t i = 0; (i < args_spec_list.size()) && (i < 4); i++) {
    MS_EXCEPTION_IF_NULL(args_spec_list[i]);
    hash_value = hash_combine(hash_value, args_spec_list[i]->hash());
  }
  return hash_value;
}

bool AbstractBasePtrListDeepEqual(const AbstractBasePtrList &lhs, const AbstractBasePtrList &rhs) {
  if (lhs.size() != rhs.size()) {
    return false;
  }
  std::size_t size = lhs.size();
  for (std::size_t i = 0; i < size; i++) {
    MS_EXCEPTION_IF_NULL(lhs[i]);
    MS_EXCEPTION_IF_NULL(rhs[i]);
    if (!(*lhs[i] == *rhs[i])) {
      return false;
    }
  }
  return true;
}

std::size_t AbstractBasePtrListHasher::operator()(const AbstractBasePtrList &args_spec_list) const {
  return AbstractBasePtrListHash(args_spec_list);
}

bool AbstractBasePtrListEqual::operator()(const AbstractBasePtrList &lhs, const AbstractBasePtrList &rhs) const {
  return AbstractBasePtrListDeepEqual(lhs, rhs);
}
}  // namespace abstract
}  // namespace mindspore
