pub(crate) fn eval_eq2(&mut self, rhs: Value, lhs: Value) -> Result<bool, RubyError> {
        if let Some(lhsi) = lhs.as_fixnum() {
            if let Some(rhsi) = rhs.as_fixnum() {
                return Ok(lhsi == rhsi);
            } else if let Some(rhsf) = rhs.as_flonum() {
                return Ok(lhsi as f64 == rhsf);
            }
        } else if let Some(lhsf) = lhs.as_flonum() {
            if let Some(rhsf) = rhs.as_flonum() {
                if lhsf.is_nan() && rhsf.is_nan() {
                    return Ok(false);
                } else {
                    return Ok(lhsf == rhsf);
                }
            } else if let Some(rhsi) = rhs.as_fixnum() {
                return Ok(rhsi as f64 == lhsf);
            }
        }
        if rhs.is_packed_value() || lhs.is_packed_value() {
            return Ok(lhs.id() == rhs.id());
        }
        if lhs.id() == rhs.id() {
            return Ok(true);
        };
        let (lhsr, rhsr) = (lhs.rvalue(), rhs.rvalue());
        match (lhsr.kind(), rhsr.kind()) {
            (ObjKind::BIGNUM, ObjKind::BIGNUM) => Ok(*lhsr.bignum() == *rhsr.bignum()),
            (ObjKind::FLOAT, ObjKind::FLOAT) => Ok(lhsr.float() == rhsr.float()),
            (ObjKind::BIGNUM, ObjKind::FLOAT) => {
                Ok(lhsr.bignum().to_f64().unwrap() == rhsr.float())
            }
            (ObjKind::FLOAT, ObjKind::BIGNUM) => {
                Ok(lhsr.float() == rhsr.bignum().to_f64().unwrap())
            }
            (ObjKind::COMPLEX, ObjKind::COMPLEX) => {
                let RubyComplex { r: r1, i: i1 } = *lhsr.complex();
                let RubyComplex { r: r2, i: i2 } = *rhsr.complex();
                Ok(r1.to_real() == r2.to_real() && i1.to_real() == i2.to_real())
            }
            (ObjKind::STRING, ObjKind::STRING) => {
                Ok(lhsr.string().as_bytes() == rhsr.string().as_bytes())
            }
            (ObjKind::ARRAY, ObjKind::ARRAY) => {
                let (lhs, rhs) = (&*lhsr.array(), &*rhsr.array());
                if lhs.len() != rhs.len() {
                    return Ok(false);
                }
                for (l, r) in lhs.iter().zip(rhs.iter()) {
                    if !self.eval_eq2(*r, *l)? {
                        return Ok(false);
                    }
                }
                Ok(true)
            }
            (ObjKind::RANGE, ObjKind::RANGE) => {
                let (lhs, rhs) = (&*lhsr.range(), &*rhsr.range());
                Ok(rhs.exclude == lhs.exclude
                    && self.eval_eq2(rhs.start, lhs.start)?
                    && self.eval_eq2(rhs.end, lhs.end)?)
            }
            (ObjKind::HASH, ObjKind::HASH) => Ok(*lhsr.rhash() == *rhsr.rhash()),
            (ObjKind::REGEXP, ObjKind::REGEXP) => Ok(*lhsr.regexp() == *rhsr.regexp()),
            (ObjKind::TIME, ObjKind::TIME) => Ok(*lhsr.time() == *rhsr.time()),
            (ObjKind::INVALID, _) | (_, ObjKind::INVALID) => {
                return Err(RubyError::internal(format!(
                    "Invalid rvalue. (maybe GC problem) {:?} {:?}",
                    lhs.rvalue(),
                    rhs.rvalue()
                )))
            }
            (_, _) => match self.eval_send1(IdentId::_EQ, lhs, rhs) {
                Ok(v) => Ok(v.to_bool()),
                _ => Ok(false),
            },
        }
    }
Equality of Value.

This kind of equality is used for `==` method (or operator) of Ruby.
Generally, objects that are considered to have a same value are `==`.
Numeric which have a same mathematical value are `==`.
String or Symbol which indicate an identical string or symbol are `==`.
Some classes have original difinitions of `==`.

ex. 3.0 == 3.
ruruby/src/vm/executor/ops.rs