Notifications
Clear all

[Closed] Universal GET HASH

as you know some types of max values are not ‘hashable’ (nodes, structures, names, etc.).
and i’ve tried to write an ‘universal’ gethashvalue function.

here is what i have:

fn getValueHash val hash:0 = 
(
	fn getStructHash str hash:0 = 
	(
		for p in (getpropnames str) do hash = getvaluehash (getproperty str p) hash:hash
		hash
	)
	if val != undefined do case of
	(
		 (isstruct val): hash = getstructhash val hash:hash
   (iskindof val Array): for v in val do hash = getvaluehash v hash:hash 
	   (isvalidobj val): hash = gethashvalue (gethandlebyanim val) hash
				default: hash = if (h = gethashvalue val hash) == undefined then gethashvalue (val as string) hash else h
	)
	hash
)  

have i missed anything? any ideas, comments, improvements are welcome

9 Replies

there is another case:

(iskindof val dotnetobject or iskindof val dotnetcontrol): hash = gethashvalue (val.GetHashCode()) hash:hash 
 lo1

Looks pretty thorough.

I’m only wondering about this part:

 (isvalidobj val): hash = gethashvalue (gethandlebyanim val) hash

Isn’t getHandleByAnim already a sufficient hash value?

edit: oh sorry I misread it, nm, it’s fine

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

getHandleByAnim is more like a number in creation order. it’s a unique number for current scene and technically can be used as hash value. but i want to get it made using previous hash.

there is a problem in my maxobject hash calculation… some object can have the same code as just an integer that equals object’s anim handle.
probably we have to involve object class in hashing.

Great idea, however, I am unsure of how to properly use the gethashvalue.

I assumed that I could for instance use it to compare two bitarrays.

Let’s say I have 1000 bitarrays and I want to know if some are repeated. Instead doing a string comparison, I thought I could retrieve a unique hash from for each bitarray and then compare them like:

gethashvalue #{1,5..15} 0

But that doesn’t seem to be right.
Which is the correct way to use it?

showing the nuts and bolts may shed some light on how best to use it

// methods for calculating hash value for various class values

#define HASHPRECISION 6 

// form the hash value for string s
inline void Hash(TCHAR* s, UINT& hashVal)
{
	for (; *s; s++)
		hashVal = *s + 31 * hashVal;
	return;
}

// form the hash value for integer i
inline void Hash(int i, UINT& hashVal)
{
	hashVal = i + 31 * hashVal;
	return;
}

// decompose a float into integer and exponent given precision
inline void decomposeFloat(float v, int& mantissa, int& exponent)
{
	if (v == 0.0f) {
		exponent = mantissa = 0;
		return;
	}
	BOOL neg = FALSE;
	if (v < 0.0f) {
		neg = TRUE;
		v = fabsf(v);
	}
	exponent = int(log10f (v));
	if (v >= 1.0f) exponent += 1;
	mantissa = int(v*(float(10^(HASHPRECISION-exponent)) +0.5f));
	return;
}

// form the hash value for float f
inline void Hash(float f, UINT& hashVal)
{
	int mantissa, exponent;
	decomposeFloat(f, mantissa, exponent);
	Hash(mantissa, hashVal);
	Hash(exponent, hashVal);
	return;
}

// form the hash value for Point2 p
inline void Hash(Point2 p, UINT& hashVal)
{
	Hash(p.x, hashVal);
	Hash(p.y, hashVal);
	return;
}

// form the hash value for Point3 p
inline void Hash(Point3 p, UINT& hashVal)
{
	Hash(p.x, hashVal);
	Hash(p.y, hashVal);
	Hash(p.z, hashVal);
	return;
}

// form the hash value for AColor c
inline void Hash(AColor c, UINT& hashVal)
{
	Hash(c.r, hashVal);
	Hash(c.g, hashVal);
	Hash(c.b, hashVal);
	Hash(c.a, hashVal);
	return;
}

// form the hash value for Quat q
inline void Hash(Quat q, UINT& hashVal)
{
	Hash(q.x, hashVal);
	Hash(q.y, hashVal);
	Hash(q.z, hashVal);
	Hash(q.w, hashVal);
	return;
}

// form the hash value for Angle Axis a
inline void Hash(AngAxis a, UINT& hashVal)
{
	Hash(a.axis, hashVal);
	Hash(a.angle, hashVal);
	return;
}

// form the hash value for Matrix3 m
inline void Hash(Matrix3 m, UINT& hashVal)
{
	Hash(m.GetRow(0), hashVal);
	Hash(m.GetRow(1), hashVal);
	Hash(m.GetRow(2), hashVal);
	Hash(m.GetRow(3), hashVal);
	return;
}

// form the hash value for Interval t
inline void Hash(Interval t, UINT& hashVal)
{
	Hash(t.Start(), hashVal);
	Hash(t.End(), hashVal);
	return;
}

// form the hash value for Ray r
inline void Hash(Ray r, UINT& hashVal)
{
	Hash(r.p, hashVal);
	Hash(r.dir, hashVal);
	return;
}

// form the hash value for BitArray b
inline void Hash(BitArray b, UINT& hashVal)
{
	for (int i = 0; i < b.GetSize(); i++) {
		if (b[i]) Hash(i, hashVal);
	}
	return;
}

BOOL 
getHashValue(Value* val, UINT& HashValue)
{
	if (is_float_number(val)) 
		Hash(val->to_float(),HashValue);
	else if (is_integer_number(val)) 
		Hash(val->to_int(),HashValue);
	else if (val->is_kind_of(class_tag(String))) 
		Hash(val->to_string(),HashValue);
	else if (val->is_kind_of(class_tag(Array))) {
		Array* theArray = (Array*)val;
		for (int i = 0; i < theArray->size; i++) {
			BOOL ok=getHashValue(theArray->data[i],HashValue);
			if (!ok) return FALSE;
		}
	}
	else if (val->is_kind_of(class_tag(BitArrayValue))) 
		Hash(val->to_bitarray(),HashValue);
	else if (val->is_kind_of(class_tag(Point3Value))) 
		Hash(val->to_point3(),HashValue);
	else if (val->is_kind_of(class_tag(RayValue))) 
		Hash(val->to_ray(),HashValue);
	else if (val->is_kind_of(class_tag(QuatValue))) 
		Hash(val->to_quat(),HashValue);
	else if (val->is_kind_of(class_tag(AngAxisValue))) 
		Hash(val->to_angaxis(),HashValue);
	else if (val->is_kind_of(class_tag(EulerAnglesValue))) {
		Hash(((EulerAnglesValue*)val)->angles[0],HashValue);
		Hash(((EulerAnglesValue*)val)->angles[1],HashValue);
		Hash(((EulerAnglesValue*)val)->angles[2],HashValue);
	}
	else if (val->is_kind_of(class_tag(Matrix3Value))) 
		Hash(val->to_matrix3(),HashValue);
	else if (val->is_kind_of(class_tag(Point2Value))) 
		Hash(val->to_point2(),HashValue);
	else if (val->is_kind_of(class_tag(ColorValue))) 
		Hash(val->to_acolor(),HashValue);
	else
		return FALSE;
	return TRUE;
}

// the MAXScript interface

Value* 
getHashValue_cf(Value** arg_list, int count)
{//	getHashValue <value>  
	check_arg_count_with_keys(getHashValue, 2, count);
	UINT HashValue = (UINT)arg_list[1]->to_int();
	return (getHashValue(arg_list[0], HashValue) ? Integer::intern((int)HashValue) : &undefined);
}

Thank you Klunk. Looking at that code I see how it would never work well for the bitarrays comparison I was looking for.

Perhaps something like this would return a unique hash then:

ba = #{1,5..15}
 gethashvalue ba ba.count

you are right… there is a problem with hash formula for bitarrays.
probably it should be:

inline void Hash(BitArray b, UINT& hashVal)
  {
 	     for (int i = 0; i < b.GetSize(); i++) {
 		         if (b[i]) Hash(i+1, hashVal);
 	     }
 	     return;
  }
1 Reply
(@polytools3d)
Joined: 11 months ago

Posts: 0

Not only for bitarrays, for arrays too. Haven’t tested all types, but perhaps the issue (if we can call it an issue) affect other types too.

gethashvalue #{5, 38} 0
      161
      gethashvalue #{6..7} 0
      161
      
      gethashvalue #(5, 38) 0
      193
      gethashvalue #(6, 7) 0
      193

By the way, the code I posted before does not work either:

ba = #{1,5..15}
     gethashvalue ba ba.count  -- Can't rely on this either

Considering this, I wonder what is gethashvalue function useful for.

Do you guys know of a fast accurate way of getting a unique Hash or ID that do not take into account the order the values in an array?

For example:
#(1,5,9) and #(5,9,1) should return the same Hash/ID