[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
there is another case:
(iskindof val dotnetobject or iskindof val dotnetcontrol): hash = gethashvalue (val.GetHashCode()) hash:hash
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
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.
Lets 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 doesnt 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;
}
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