#ifndef MVR_H_INCLUDED #define MVR_H_INCLUDED #if (defined(__GNUC__) && (__GNUC__ >= 4)) #define MVR_LIKELY(a) __builtin_expect((a), 1) #define MVR_UNLIKELY(a) __builtin_expect((a), 0) #else #define MVR_LIKELY(a) (a) #define MVR_UNLIKELY(a) (a) #endif typedef AV *mvr; static HV *mvr_stash_cache = NULL; static void mvr_on_boot(pTHX) { mvr_stash_cache = gv_stashpv("Math::Vector::Real", GV_ADD); } static void mvr_on_clone(pTHX) { mvr_stash_cache = NULL; } static I32 mvr_len(pTHX_ mvr v) { return av_len(v); } static void mvr_check_len(pTHX_ mvr av, I32 len) { if (MVR_UNLIKELY(len != mvr_len(aTHX_ av))) croak("vector dimensions do not match"); } static int mvr_regular(pTHX_ mvr av) { return (!MVR_UNLIKELY(SvRMAGICAL(av)) && !MVR_UNLIKELY(AvREIFY(av))); } #define MVR_REGULAR (MVR_LIKELY(mvr_regular(aTHX_ v))) #define MVR_REGULAR2 (MVR_LIKELY(MVR_LIKELY(mvr_regular(aTHX_ v0)) && \ MVR_LIKELY(mvr_regular(aTHX_ v1)))) #define MVR_REGULAR4 (MVR_LIKELY(MVR_LIKELY(mvr_regular(aTHX_ a0)) && \ MVR_LIKELY(mvr_regular(aTHX_ a1)) && \ MVR_LIKELY(mvr_regular(aTHX_ b0)) && \ MVR_LIKELY(mvr_regular(aTHX_ b1)))) #ifdef SvNOK_nog #define MVR_SvNV(a) (LIKELY(SvNOK_nog(a) != 0) ? SvNVX(a) : sv_2nv(a)) #else #define MVR_SvNV(a) (SvNV(a)) #endif static NV mvr_get(pTHX_ mvr av, I32 ix) { SV **svp = av_fetch(av, ix, 0); if (MVR_LIKELY(svp != 0)) return MVR_SvNV(*svp); return 0; } static SV ** mvr_get_svp_fast(pTHX_ mvr av) { return AvARRAY(av); } static NV mvr_get_fast(pTHX_ SV **svp, I32 ix) { SV *sv = svp[ix]; return (MVR_LIKELY(sv != NULL) ? MVR_SvNV(sv) : 0.0); } void mvr_set(pTHX_ mvr av, I32 ix, NV nv) { av_store(av, ix, newSVnv(nv)); } static SV* mvr_get_sv(pTHX_ mvr av, I32 ix) { SV **svp = av_fetch(av, ix, 1); if (MVR_UNLIKELY(svp == NULL)) croak("unable to get lvalue element from array"); return *svp; } static SV* mvr_get_sv_fast(pTHX_ mvr av, SV **svp, I32 ix) { SV *sv = svp[ix]; return (MVR_LIKELY(sv != NULL) ? sv : mvr_get_sv(aTHX_ av, ix)); } static mvr mvr_from_sv(pTHX_ SV *sv) { if (MVR_LIKELY(SvROK(sv))) { mvr av = (mvr )SvRV(sv); if (MVR_LIKELY(SvTYPE((SV *)av) == SVt_PVAV)) return av; } croak("argument is not an object of class Math::Vector::Real or can not be coerced into one"); } static void sv_set_mvr(pTHX_ SV *sv, mvr av) { HV *stash; #if (PERL_VERSION < 12) sv_upgrade(sv, SVt_RV); #else sv_upgrade(sv, SVt_IV); #endif SvTEMP_off((SV*)av); SvRV_set(sv, (SV*)(av)); SvROK_on(sv); stash = (mvr_stash_cache ? mvr_stash_cache : gv_stashpv("Math::Vector::Real", GV_ADD)); sv_bless(sv, stash); } static mvr mvr_new(pTHX_ I32 len) { mvr av = newAV(); av_extend(av, len); return av; } static mvr mvr_clone(pTHX_ mvr v, I32 len) { I32 i; mvr av = mvr_new(aTHX_ len); if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) mvr_set(aTHX_ av, i, mvr_get_fast(aTHX_ svp, i)); } else { for (i = 0; i <= len; i++) mvr_set(aTHX_ av, i, mvr_get(aTHX_ v, i)); } return av; } static mvr mvr_2mortal(pTHX_ mvr v) { return (mvr)sv_2mortal((SV*)v); } static int mvr_equal(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) if (mvr_get_fast(aTHX_ svp0, i) != mvr_get_fast(aTHX_ svp1, i)) return 0; } else { for (i = 0; i <= len; i++) if (mvr_get(aTHX_ v0, i) != mvr_get(aTHX_ v1, i)) return 0; } return 1; } static void mvr_add(pTHX_ mvr v0, mvr v1, I32 len, mvr out) { I32 i; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, mvr_get_fast(aTHX_ svp0, i) + mvr_get_fast(aTHX_ svp1, i)); } else { for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, mvr_get(aTHX_ v0, i) + mvr_get(aTHX_ v1, i)); } } static void mvr_add_me(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv_fast(aTHX_ v0, svp0, i); sv_setnv(sv, MVR_SvNV(sv) + mvr_get_fast(aTHX_ svp1, i)); } } else { for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv(aTHX_ v0, i); sv_setnv(sv, MVR_SvNV(sv) + mvr_get(aTHX_ v1, i)); } } } static void mvr_neg(pTHX_ mvr v, I32 len, mvr out) { I32 i; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, -mvr_get_fast(aTHX_ svp, i)); } else { for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, -mvr_get(aTHX_ v, i)); } } static void mvr_neg_me(pTHX_ mvr v, I32 len) { I32 i; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv_fast(aTHX_ v, svp, i); sv_setnv(sv, -MVR_SvNV(sv)); } } else { for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv(aTHX_ v, i); sv_setnv(sv, -MVR_SvNV(sv)); } } } static void mvr_subtract(pTHX_ mvr v0, mvr v1, I32 len, mvr out) { /* out = v0 - v1 */ I32 i; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, mvr_get_fast(aTHX_ svp0, i) - mvr_get_fast(aTHX_ svp1, i)); } else { for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, mvr_get(aTHX_ v0, i) - mvr_get(aTHX_ v1, i)); } } static void mvr_subtract_me(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv_fast(aTHX_ v0, svp0, i); sv_setnv(sv, MVR_SvNV(sv) - mvr_get_fast(aTHX_ svp1, i)); } } else { for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv(aTHX_ v0, i); sv_setnv(sv, MVR_SvNV(sv) - mvr_get(aTHX_ v1, i)); } } } static NV mvr_dist2(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; NV d2 = 0; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) { NV delta = mvr_get_fast(aTHX_ svp0, i) - mvr_get_fast(aTHX_ svp1, i); d2 += delta * delta; } } else { for (i = 0; i <= len; i++) { NV delta = mvr_get(aTHX_ v0, i) - mvr_get(aTHX_ v1, i); d2 += delta * delta; } } return d2; } static NV mvr_dist(pTHX_ mvr v0, mvr v1, I32 len) { return sqrt(mvr_dist2(aTHX_ v0, v1, len)); } static NV mvr_manhattan_dist(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; NV d = 0; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) d += fabs(mvr_get_fast(aTHX_ svp0, i) - mvr_get_fast(aTHX_ svp1, i)); } else { for (i = 0; i <= len; i++) d += fabs(mvr_get(aTHX_ v0, i) - mvr_get(aTHX_ v1, i)); } return d; } static NV mvr_chebyshev_dist(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; NV max = 0; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) { NV d = fabs(mvr_get_fast(aTHX_ svp0, i) - mvr_get_fast(aTHX_ svp1, i)); if (d > max) max = d; } } else { for (i = 0; i <= len; i++) { NV d = fabs(mvr_get(aTHX_ v0, i) - mvr_get(aTHX_ v1, i)); if (d > max) max = d; } } return max; } static NV mvr_dot_product(pTHX_ mvr v0, mvr v1, I32 len) { I32 i; NV acu; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (acu = 0, i = 0; i <= len; i++) acu += mvr_get_fast(aTHX_ svp0, i) * mvr_get_fast(aTHX_ svp1, i); } else { for (acu = 0, i = 0; i <= len; i++) acu += mvr_get(aTHX_ v0, i) * mvr_get(aTHX_ v1, i); } return acu; } static void mvr_scalar_product(pTHX_ mvr v, NV s, I32 len, mvr out) { I32 i; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, s * mvr_get_fast(aTHX_ svp, i)); } else { for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, s * mvr_get(aTHX_ v, i)); } } mvr_scalar_product_me(pTHX_ mvr v, NV scl, I32 len) { I32 i; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv_fast(aTHX_ v, svp, i); sv_setnv(sv, scl * MVR_SvNV(sv)); } } else { for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv(aTHX_ v, i); sv_setnv(sv, scl * MVR_SvNV(sv)); } } } static void mvr_subtract_and_neg_me(pTHX_ mvr v0, mvr v1, I32 len) { /* v0 = v1 - v0 */ I32 i; if (MVR_REGULAR2) { SV **svp0 = mvr_get_svp_fast(aTHX_ v0); SV **svp1 = mvr_get_svp_fast(aTHX_ v1); for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv_fast(aTHX_ v0, svp0, i); sv_setnv(sv, mvr_get_fast(aTHX_ svp1, i) - MVR_SvNV(sv)); } } else { for (i = 0; i <= len; i++) { SV *sv = mvr_get_sv(aTHX_ v0, i); sv_setnv(sv, mvr_get(aTHX_ v1, i) - MVR_SvNV(sv)); } } } static NV mvr_norm2(pTHX_ mvr v, I32 len) { I32 i; NV acu; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0, acu = 0; i <= len; i++) { NV c = mvr_get_fast(aTHX_ svp, i); acu += c * c; } } else { for (i = 0, acu = 0; i <= len; i++) { NV c = mvr_get(aTHX_ v, i); acu += c * c; } } return acu; } static NV mvr_norm(pTHX_ mvr v, I32 len) { return sqrt(mvr_norm2(aTHX_ v, len)); } static NV mvr_manhattan_norm(pTHX_ mvr v, I32 len) { I32 i; NV acu; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0, acu = 0; i <= len; i++) acu += fabs(mvr_get_fast(aTHX_ svp, i)); } else { for (i = 0, acu = 0; i <= len; i++) acu += fabs(mvr_get(aTHX_ v, i)); } return acu; } static I32 mvr_min_component_index(pTHX_ mvr v, I32 len) { I32 i; I32 best = 0; NV min; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); min = fabs(mvr_get_fast(aTHX_ svp, best)); for (i = 1; i <= len; i++) { NV c = fabs(mvr_get_fast(aTHX_ svp, i)); if (c < min) { min = c; best = i; } } } else { min = fabs(mvr_get(aTHX_ v, best)); for (i = 1; i <= len; i++) { NV c = fabs(mvr_get(aTHX_ v, i)); if (c < min) { min = c; best = i; } } } return best; } static I32 mvr_max_component_index(pTHX_ mvr v, I32 len) { I32 i; I32 best = 0; NV max = 0; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) { NV c = fabs(mvr_get_fast(aTHX_ svp, i)); if (c > max) { max = c; best = i; } } } else { for (i = 0; i <= len; i++) { NV c = fabs(mvr_get(aTHX_ v, i)); if (c > max) { max = c; best = i; } } } return best; } static void mvr_axis_versor(pTHX_ I32 len, I32 axis, mvr out) { I32 i; for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, (i == axis ? 1 : 0)); } static void mvr_cross_product_3d(pTHX_ mvr v0, mvr v1, mvr out) { I32 i; NV x0 = mvr_get(aTHX_ v0, 0); NV y0 = mvr_get(aTHX_ v0, 1); NV z0 = mvr_get(aTHX_ v0, 2); NV x1 = mvr_get(aTHX_ v1, 0); NV y1 = mvr_get(aTHX_ v1, 1); NV z1 = mvr_get(aTHX_ v1, 2); mvr_set(aTHX_ out, 0, y0 * z1 - y1 * z0); mvr_set(aTHX_ out, 1, z0 * x1 - z1 * x0); mvr_set(aTHX_ out, 2, x0 * y1 - x1 * y0); } static void mvr_versor_unsafe(pTHX_ mvr v, I32 len, mvr out) { mvr_scalar_product(aTHX_ v, 1.0 / mvr_norm(aTHX_ v, len), len, out); } static void mvr_versor_me_unsafe(pTHX_ mvr v, I32 len) { NV inr = 1.0 / mvr_norm(aTHX_ v, len); mvr_scalar_product_me(aTHX_ v, inr, len); } static void mvr_first_orthant_reflection(pTHX_ mvr v, I32 len, mvr out) { I32 i; if (MVR_REGULAR) { SV **svp = mvr_get_svp_fast(aTHX_ v); for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, fabs(mvr_get_fast(aTHX_ svp, i))); } else { for (i = 0; i <= len; i++) mvr_set(aTHX_ out, i, fabs(mvr_get(aTHX_ v, i))); } } static NV mvr_max_dist2_between_boxes(pTHX_ mvr a0, mvr a1, mvr b0, mvr b1, I32 len) { I32 i; NV d2 = 0; if (MVR_REGULAR4) { SV **svpa0 = mvr_get_svp_fast(aTHX_ a0); SV **svpa1 = mvr_get_svp_fast(aTHX_ a1); SV **svpb0 = mvr_get_svp_fast(aTHX_ b0); SV **svpb1 = mvr_get_svp_fast(aTHX_ b1); for (i = 0; i <= len; i++) { NV na0 = mvr_get_fast(aTHX_ svpa0, i); NV na1 = mvr_get_fast(aTHX_ svpa1, i); NV nb0 = mvr_get_fast(aTHX_ svpb0, i); NV nb1 = mvr_get_fast(aTHX_ svpb1, i); NV d0, d1; if (MVR_UNLIKELY(na0 > na1)) { NV tmp = na1; na1 = na0; na0 = tmp; } if (MVR_UNLIKELY(nb0 > nb1)) { NV tmp = nb1; nb1 = nb0; nb0 = tmp; } d0 = nb0 - na1; d1 = nb1 - na0; d0 *= d0; d1 *= d1; d2 += (d0 > d1 ? d0 : d1); } return d2; } else { for (i = 0; i <= len; i++) { NV na0 = mvr_get(aTHX_ a0, i); NV na1 = mvr_get(aTHX_ a1, i); NV nb0 = mvr_get(aTHX_ b0, i); NV nb1 = mvr_get(aTHX_ b1, i); NV d0, d1; if (na0 > na1) { NV tmp = na1; na1 = na0; na0 = tmp; } if (nb0 > nb1) { NV tmp = nb1; nb1 = nb0; nb0 = tmp; } d0 = nb0 - na1; d1 = nb1 - na0; d0 *= d0; d1 *= d1; d2 += (d0 > d1 ? d0 : d1); } } return d2; } static NV mvr_dist2_between_boxes(pTHX_ mvr a0, mvr a1, mvr b0, mvr b1, I32 len) { I32 i; NV d2 = 0; if (MVR_REGULAR4) { SV **svpa0 = mvr_get_svp_fast(aTHX_ a0); SV **svpa1 = mvr_get_svp_fast(aTHX_ a1); SV **svpb0 = mvr_get_svp_fast(aTHX_ b0); SV **svpb1 = mvr_get_svp_fast(aTHX_ b1); for (i = 0; i <= len; i++) { NV na0 = mvr_get_fast(aTHX_ svpa0, i); NV na1 = mvr_get_fast(aTHX_ svpa1, i); NV nb0 = mvr_get_fast(aTHX_ svpb0, i); NV nb1 = mvr_get_fast(aTHX_ svpb1, i); NV d0; if (MVR_UNLIKELY(na0 > na1)) { NV tmp = na1; na1 = na0; na0 = tmp; } if (MVR_UNLIKELY(nb0 > nb1)) { NV tmp = nb1; nb1 = nb0; nb0 = tmp; } d0 = na0 - nb1; if (d0 >= 0) d2 += d0 * d0; else { NV d1 = nb0 - na1; if (d1 > 0) d2 += d1 * d1; } } } else { for (i = 0; i <= len; i++) { NV na0 = mvr_get(aTHX_ a0, i); NV na1 = mvr_get(aTHX_ a1, i); NV nb0 = mvr_get(aTHX_ b0, i); NV nb1 = mvr_get(aTHX_ b1, i); NV d0; if (na0 > na1) { NV tmp = na1; na1 = na0; na0 = tmp; } if (nb0 > nb1) { NV tmp = nb1; nb1 = nb0; nb0 = tmp; } d0 = na0 - nb1; if (d0 >= 0) d2 += d0 * d0; else { NV d1 = nb0 - na1; if (d1 > 0) d2 += d1 * d1; } } } return d2; } #endif