source: git/src/netbits.c @ be4f785

stereo-2025
Last change on this file since be4f785 was c77682a, checked in by Olly Betts <olly@…>, 5 months ago

Stop reporting node stats

These are kind of interesting, but since the advent of surveying with
Disto-X and similar devices which make it quick to shoot multiple
splay legs from each station the number of larger order nodes has
increased and this information is now quite verbose and any utility
it had has substantially declined.

If they're still wanted they could make a reappearance in the future in
aven, with splays excluded when counting the number of legs at each
station.

Fixes #86, reported by Wookey.

  • Property mode set to 100644
File size: 23.0 KB
Line 
1/* netbits.c
2 * Miscellaneous primitive network routines for Survex
3 * Copyright (C) 1992-2024 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <config.h>
21
22#if 0
23# define DEBUG_INVALID 1
24#endif
25
26#include "debug.h"
27#include "cavern.h"
28#include "filename.h"
29#include "message.h"
30#include "netbits.h"
31#include "datain.h" /* for compile_error */
32#include "validate.h" /* for compile_error */
33#include <math.h>
34
35#define THRESHOLD (REAL_EPSILON * 1000) /* 100 was too small */
36
37node *stn_iter = NULL; /* for FOR_EACH_STN */
38
39static struct {
40   prefix * to_name;
41   prefix * fr_name;
42   linkfor * leg;
43   int n;
44} last_leg = { NULL, NULL, NULL, 0 };
45
46void clear_last_leg(void) {
47   last_leg.to_name = NULL;
48}
49
50static char freeleg(node **stnptr);
51
52#ifdef NO_COVARIANCES
53static void check_var(const var *v) {
54   int bad = 0;
55
56   for (int i = 0; i < 3; i++) {
57      if (isnan(v[i])
58         printf("*** NaN!!!\n"), bad = 1;
59   }
60   if (bad) print_var(v);
61   return;
62}
63#else
64#define V(A,B) ((*v)[A][B])
65static void check_var(const var *v) {
66   int bad = 0;
67   int ok = 0;
68#if DEBUG_INVALID
69   real det = 0.0;
70#endif
71
72   for (int i = 0; i < 3; i++) {
73      for (int j = 0; j < 3; j++) {
74         if (isnan(V(i, j)))
75            printf("*** NaN!!!\n"), bad = 1, ok = 1;
76         if (V(i, j) != 0.0) ok = 1;
77      }
78   }
79   if (!ok) return; /* ignore all-zero matrices */
80
81#if DEBUG_INVALID
82   for (int i = 0; i < 3; i++) {
83      det += V(i, 0) * (V((i + 1) % 3, 1) * V((i + 2) % 3, 2) -
84                        V((i + 1) % 3, 2) * V((i + 2) % 3, 1));
85   }
86
87   if (fabs(det) < THRESHOLD)
88      printf("*** Singular!!!\n"), bad = 1;
89#endif
90
91#if 0
92   /* don't check this - it isn't always the case! */
93   if (fabs(V(0,1) - V(1,0)) > THRESHOLD ||
94       fabs(V(0,2) - V(2,0)) > THRESHOLD ||
95       fabs(V(1,2) - V(2,1)) > THRESHOLD)
96      printf("*** Not symmetric!!!\n"), bad = 1;
97   if (V(0,0) <= 0.0 || V(1,1) <= 0.0 || V(2,2) <= 0.0)
98      printf("*** Not positive definite (diag <= 0)!!!\n"), bad = 1;
99   if (sqrd(V(0,1)) >= V(0,0)*V(1,1) || sqrd(V(0,2)) >= V(0,0)*V(2,2) ||
100       sqrd(V(1,0)) >= V(0,0)*V(1,1) || sqrd(V(2,0)) >= V(0,0)*V(2,2) ||
101       sqrd(V(1,2)) >= V(2,2)*V(1,1) || sqrd(V(2,1)) >= V(2,2)*V(1,1))
102      printf("*** Not positive definite (off diag^2 >= diag product)!!!\n"), bad = 1;
103#endif
104   if (bad) print_var(*v);
105}
106
107#define SN(V,A,B) ((*(V))[(A)==(B)?(A):2+(A)+(B)])
108#define S(A,B) SN(v,A,B)
109
110static void check_svar(const svar *v) {
111   int bad = 0;
112   int ok = 0;
113#if DEBUG_INVALID
114   real det = 0.0;
115#endif
116
117   for (int i = 0; i < 6; i++) {
118      if (isnan((*v)[i]))
119         printf("*** NaN!!!\n"), bad = 1, ok = 1;
120      if ((*v)[i] != 0.0) ok = 1;
121   }
122   if (!ok) return; /* ignore all-zero matrices */
123
124#if DEBUG_INVALID
125   for (int i = 0; i < 3; i++) {
126      det += S(i, 0) * (S((i + 1) % 3, 1) * S((i + 2) % 3, 2) -
127                        S((i + 1) % 3, 2) * S((i + 2) % 3, 1));
128   }
129
130   if (fabs(det) < THRESHOLD)
131      printf("*** Singular!!!\n"), bad = 1;
132#endif
133
134#if 0
135   /* don't check this - it isn't always the case! */
136   if ((*v)[0] <= 0.0 || (*v)[1] <= 0.0 || (*v)[2] <= 0.0)
137      printf("*** Not positive definite (diag <= 0)!!!\n"), bad = 1;
138   if (sqrd((*v)[3]) >= (*v)[0]*(*v)[1] ||
139       sqrd((*v)[4]) >= (*v)[0]*(*v)[2] ||
140       sqrd((*v)[5]) >= (*v)[1]*(*v)[2])
141      printf("*** Not positive definite (off diag^2 >= diag product)!!!\n"), bad = 1;
142#endif
143   if (bad) print_svar(*v);
144}
145#endif
146
147static void check_d(const delta *d) {
148   int bad = 0;
149
150   for (int i = 0; i < 3; i++) {
151      if (isnan((*d)[i]))
152         printf("*** NaN!!!\n"), bad = 1;
153   }
154
155   if (bad) printf("(%4.2f,%4.2f,%4.2f)\n", (*d)[0], (*d)[1], (*d)[2]);
156}
157
158/* insert at head of double-linked list */
159void
160add_stn_to_list(node **list, node *stn) {
161   SVX_ASSERT(list);
162   SVX_ASSERT(stn);
163   SVX_ASSERT(stn_iter != stn); /* if it does, we're still on a list... */
164#if 0
165   printf("add_stn_to_list(%p, [%p] ", list, stn);
166   if (stn->name) print_prefix(stn->name);
167   printf(")\n");
168#endif
169   stn->next = *list;
170   stn->prev = NULL;
171   if (*list) (*list)->prev = stn;
172   *list = stn;
173}
174
175/* remove from double-linked list */
176void
177remove_stn_from_list(node **list, node *stn) {
178   SVX_ASSERT(list);
179   SVX_ASSERT(stn);
180#if 0
181   printf("remove_stn_from_list(%p, [%p] ", list, stn);
182   if (stn->name) print_prefix(stn->name);
183   printf(")\n");
184#endif
185#if DEBUG_INVALID
186     {
187        /* Go back to the head of the list stn is actually on and
188         * check it's the same as the list we were asked to remove
189         * it from.
190         */
191        validate();
192        node *find_head = stn;
193        while (find_head->prev) {
194            find_head = find_head->prev;
195        }
196        SVX_ASSERT(find_head == *list);
197     }
198#endif
199   /* adjust the iterator if it points to the element we're deleting */
200   if (stn_iter == stn) stn_iter = stn_iter->next;
201   /* need a special case if we're removing the list head */
202   if (stn->prev == NULL) {
203      *list = stn->next;
204      if (*list) (*list)->prev = NULL;
205   } else {
206      stn->prev->next = stn->next;
207      if (stn->next) stn->next->prev = stn->prev;
208   }
209}
210
211/* Create (uses osmalloc) a forward leg containing the data in leg, or
212 * the reversed data in the reverse of leg, if leg doesn't hold data
213 */
214linkfor *
215copy_link(linkfor *leg)
216{
217   linkfor *legOut = osnew(linkfor);
218   if (data_here(leg)) {
219      for (int d = 2; d >= 0; d--) legOut->d[d] = leg->d[d];
220   } else {
221      leg = reverse_leg(leg);
222      SVX_ASSERT(data_here(leg));
223      for (int d = 2; d >= 0; d--) legOut->d[d] = -leg->d[d];
224   }
225#if 1
226# ifndef NO_COVARIANCES
227   check_svar(&(leg->v));
228   for (int i = 0; i < 6; i++) legOut->v[i] = leg->v[i];
229# else
230   for (int d = 2; d >= 0; d--) legOut->v[d] = leg->v[d];
231# endif
232#else
233   memcpy(legOut->v, leg->v, sizeof(svar));
234#endif
235   legOut->meta = pcs->meta;
236   if (pcs->meta) ++pcs->meta->ref_count;
237   return legOut;
238}
239
240/* Adds to the forward leg “leg”, the data in leg2, or the reversed data
241 * in the reverse of leg2, if leg2 doesn't hold data
242 */
243linkfor *
244addto_link(linkfor *leg, const linkfor *leg2)
245{
246   if (data_here(leg2)) {
247      adddd(&leg->d, &leg->d, &((linkfor *)leg2)->d);
248   } else {
249      leg2 = reverse_leg(leg2);
250      SVX_ASSERT(data_here(leg2));
251      subdd(&leg->d, &leg->d, &((linkfor *)leg2)->d);
252   }
253   addss(&leg->v, &leg->v, &((linkfor *)leg2)->v);
254   return leg;
255}
256
257static linkfor *
258addleg_(node *fr, node *to,
259        real dx, real dy, real dz,
260        real vx, real vy, real vz,
261#ifndef NO_COVARIANCES
262        real cyz, real czx, real cxy,
263#endif
264        int leg_flags)
265{
266   /* we have been asked to add a leg with the same node at both ends
267    * - this should be trapped by the caller */
268   SVX_ASSERT(fr->name != to->name);
269
270   linkfor *leg = osnew(linkfor);
271   linkfor *leg2 = (linkfor*)osnew(linkcommon);
272
273   int i = freeleg(&fr);
274   int j = freeleg(&to);
275
276   leg->l.to = to;
277   leg2->l.to = fr;
278   leg->d[0] = dx;
279   leg->d[1] = dy;
280   leg->d[2] = dz;
281#ifndef NO_COVARIANCES
282   leg->v[0] = vx;
283   leg->v[1] = vy;
284   leg->v[2] = vz;
285   leg->v[3] = cxy;
286   leg->v[4] = czx;
287   leg->v[5] = cyz;
288   check_svar(&(leg->v));
289#else
290   leg->v[0] = vx;
291   leg->v[1] = vy;
292   leg->v[2] = vz;
293#endif
294   leg2->l.reverse = i;
295   leg->l.reverse = j | FLAG_DATAHERE | leg_flags;
296
297   leg->l.flags = pcs->flags | (pcs->recorded_style << FLAGS_STYLE_BIT0);
298   leg->meta = pcs->meta;
299   if (pcs->meta) ++pcs->meta->ref_count;
300
301   fr->leg[i] = leg;
302   to->leg[j] = leg2;
303
304   return leg;
305}
306
307/* Add a leg between names *fr_name and *to_name
308 * If either is a three node, then it is split into two
309 * and the data structure adjusted as necessary.
310 */
311void
312addlegbyname(prefix *fr_name, prefix *to_name, bool fToFirst,
313             real dx, real dy, real dz,
314             real vx, real vy, real vz
315#ifndef NO_COVARIANCES
316             , real cyz, real czx, real cxy
317#endif
318             )
319{
320   if (to_name == fr_name) {
321      int type = pcs->from_equals_to_is_only_a_warning ? DIAG_WARN : DIAG_ERR;
322      /* TRANSLATORS: Here a "survey leg" is a set of measurements between two
323       * "survey stations".
324       *
325       * %s is replaced by the name of the station. */
326      compile_diagnostic(type, /*Survey leg with same station (“%s”) at both ends - typing error?*/50,
327                         sprint_prefix(to_name));
328      return;
329   }
330   node *to, *fr;
331   if (fToFirst) {
332      to = StnFromPfx(to_name);
333      fr = StnFromPfx(fr_name);
334   } else {
335      fr = StnFromPfx(fr_name);
336      to = StnFromPfx(to_name);
337   }
338   if (last_leg.to_name) {
339      if (last_leg.to_name == to_name && last_leg.fr_name == fr_name) {
340         /* FIXME: Not the right way to average... */
341         linkfor * leg = last_leg.leg;
342         int n = last_leg.n++;
343         leg->d[0] = (leg->d[0] * n + dx) / (n + 1);
344         leg->d[1] = (leg->d[1] * n + dy) / (n + 1);
345         leg->d[2] = (leg->d[2] * n + dz) / (n + 1);
346#ifndef NO_COVARIANCES
347         leg->v[0] = (leg->v[0] * n + vx) / (n + 1);
348         leg->v[1] = (leg->v[1] * n + vy) / (n + 1);
349         leg->v[2] = (leg->v[2] * n + vz) / (n + 1);
350         leg->v[3] = (leg->v[3] * n + cxy) / (n + 1);
351         leg->v[4] = (leg->v[4] * n + czx) / (n + 1);
352         leg->v[5] = (leg->v[5] * n + cyz) / (n + 1);
353         check_svar(&(leg->v));
354#else
355         leg->v[0] = (leg->v[0] * n + vx) / (n + 1);
356         leg->v[1] = (leg->v[1] * n + vy) / (n + 1);
357         leg->v[2] = (leg->v[2] * n + vz) / (n + 1);
358#endif
359         return;
360      }
361   }
362   cLegs++;
363
364   /* Suppress "unused fixed point" warnings for these stations. */
365   fr_name->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT);
366   to_name->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT);
367
368   last_leg.to_name = to_name;
369   last_leg.fr_name = fr_name;
370   last_leg.n = 1;
371   last_leg.leg = addleg_(fr, to, dx, dy, dz, vx, vy, vz,
372#ifndef NO_COVARIANCES
373                          cyz, czx, cxy,
374#endif
375                          0);
376}
377
378/* helper function for replace_pfx */
379static void
380replace_pfx_(node *stn, node *from, pos *pos_with, bool move_to_fixedlist)
381{
382   SVX_ASSERT(!fixed(stn));
383   if (move_to_fixedlist) {
384      SVX_ASSERT(pos_fixed(pos_with));
385      SVX_ASSERT(!fixed(stn));
386      remove_stn_from_list(&stnlist, stn);
387      add_stn_to_list(&fixedlist, stn);
388   }
389   stn->name->pos = pos_with;
390   for (int d = 0; d < 3; d++) {
391      linkfor *leg = stn->leg[d];
392      if (!leg) break;
393      node *to = leg->l.to;
394      if (to == from) continue;
395
396      if (fZeros(data_here(leg) ? &leg->v : &reverse_leg(leg)->v))
397         replace_pfx_(to, stn, pos_with, move_to_fixedlist);
398   }
399}
400
401/* We used to iterate over the whole station list (inefficient) - now we
402 * just look at any neighbouring nodes to see if they are equated */
403static void
404replace_pfx(const prefix *pfx_replace, const prefix *pfx_with,
405            bool move_to_fixedlist)
406{
407   SVX_ASSERT(pfx_replace);
408   SVX_ASSERT(pfx_with);
409   pos *pos_replace = pfx_replace->pos;
410   SVX_ASSERT(pos_replace != pfx_with->pos);
411
412   replace_pfx_(pfx_replace->stn, NULL, pfx_with->pos, move_to_fixedlist);
413
414#if DEBUG_INVALID
415   for (node *stn = stnlist; stn; stn = stn->next) {
416      SVX_ASSERT(stn->name->pos != pos_replace);
417   }
418   for (node *stn = fixedlist; stn; stn = stn->next) {
419      SVX_ASSERT(stn->name->pos != pos_replace);
420   }
421#endif
422
423   /* free the (now-unused) old pos */
424   osfree(pos_replace);
425}
426
427// Add equating leg between existing stations whose names are name1 and name2.
428void
429process_equate(prefix *name1, prefix *name2)
430{
431   clear_last_leg();
432   if (name1 == name2) {
433      /* catch something like *equate "fred fred" */
434      /* TRANSLATORS: Here "station" is a survey station, not a train station.
435       */
436      compile_diagnostic(DIAG_WARN, /*Station “%s” equated to itself*/13,
437                         sprint_prefix(name1));
438      return;
439   }
440   node *stn1 = StnFromPfx(name1);
441   node *stn2 = StnFromPfx(name2);
442   /* equate nodes if not already equated */
443   if (name1->pos != name2->pos) {
444      if (pfx_fixed(name1)) {
445         bool name2_fixed = pfx_fixed(name2);
446         if (name2_fixed) {
447            /* both are fixed, but let them off iff their coordinates match */
448            char *s = osstrdup(sprint_prefix(name1));
449            for (int d = 2; d >= 0; d--) {
450               if (name1->pos->p[d] != name2->pos->p[d]) {
451                  compile_diagnostic(DIAG_ERR, /*Tried to equate two non-equal fixed stations: “%s” and “%s”*/52,
452                                     s, sprint_prefix(name2));
453                  osfree(s);
454                  return;
455               }
456            }
457            /* TRANSLATORS: "equal" as in:
458             *
459             * *fix a 1 2 3
460             * *fix b 1 2 3
461             * *equate a b */
462            compile_diagnostic(DIAG_WARN, /*Equating two equal fixed points: “%s” and “%s”*/53,
463                               s, sprint_prefix(name2));
464            osfree(s);
465         }
466
467         /* name1 is fixed, so replace all refs to name2's pos with name1's */
468         replace_pfx(name2, name1, !name2_fixed);
469      } else {
470         /* name1 isn't fixed, so replace all refs to its pos with name2's */
471         replace_pfx(name1, name2, pfx_fixed(name2));
472      }
473
474      /* Suppress "unused fixed point" warnings for these stations. */
475      name1->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT);
476      name2->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT);
477
478      /* count equates as legs for now... */
479      cLegs++;
480      addleg_(stn1, stn2,
481              (real)0.0, (real)0.0, (real)0.0,
482              (real)0.0, (real)0.0, (real)0.0,
483#ifndef NO_COVARIANCES
484              (real)0.0, (real)0.0, (real)0.0,
485#endif
486              FLAG_FAKE);
487   }
488}
489
490/* Add a 'fake' leg (not counted or treated as a use of a fixed point) between
491 * existing stations *fr and *to (which *must* be different).
492 *
493 * If either node is a three node, then it is split into two
494 * and the data structure adjusted as necessary.
495 */
496void
497addfakeleg(node *fr, node *to,
498           real dx, real dy, real dz,
499           real vx, real vy, real vz
500#ifndef NO_COVARIANCES
501           , real cyz, real czx, real cxy
502#endif
503           )
504{
505   clear_last_leg();
506   addleg_(fr, to, dx, dy, dz, vx, vy, vz,
507#ifndef NO_COVARIANCES
508           cyz, czx, cxy,
509#endif
510           FLAG_FAKE);
511}
512
513static char
514freeleg(node **stnptr)
515{
516   node *stn = *stnptr;
517
518   if (stn->leg[0] == NULL) return 0; /* leg[0] unused */
519   if (stn->leg[1] == NULL) return 1; /* leg[1] unused */
520   if (stn->leg[2] == NULL) return 2; /* leg[2] unused */
521
522   /* All legs used, so split node in two */
523   node *newstn = osnew(node);
524   linkfor *leg = osnew(linkfor);
525   linkfor *leg2 = (linkfor*)osnew(linkcommon);
526
527   *stnptr = newstn;
528
529   add_stn_to_list(fixed(stn) ? &fixedlist : &stnlist, newstn);
530   newstn->name = stn->name;
531
532   leg->l.to = newstn;
533   leg->d[0] = leg->d[1] = leg->d[2] = (real)0.0;
534
535#ifndef NO_COVARIANCES
536   for (int i = 0; i < 6; i++) leg->v[i] = (real)0.0;
537#else
538   leg->v[0] = leg->v[1] = leg->v[2] = (real)0.0;
539#endif
540   leg->l.reverse = 1 | FLAG_DATAHERE | FLAG_FAKE;
541   leg->l.flags = pcs->flags | (pcs->recorded_style << FLAGS_STYLE_BIT0);
542
543   leg2->l.to = stn;
544   leg2->l.reverse = 0;
545
546   // NB this preserves pos->stn->leg[0] pointing to the "real" fixed point
547   // for stations fixed with error estimates.
548   newstn->leg[0] = stn->leg[0];
549   // Update the reverse leg.
550   reverse_leg(newstn->leg[0])->l.to = newstn;
551   newstn->leg[1] = leg2;
552
553   stn->leg[0] = leg;
554
555   newstn->leg[2] = NULL; /* needed as newstn->leg[dirn]==NULL indicates unused */
556
557   return 2; /* leg[2] unused */
558}
559
560node *
561StnFromPfx(prefix *name)
562{
563   if (name->stn != NULL) return name->stn;
564   node *stn = osnew(node);
565   stn->name = name;
566   bool fixed = false;
567   if (name->pos == NULL) {
568      name->pos = osnew(pos);
569      unfix(stn);
570   } else {
571      fixed = pfx_fixed(name);
572   }
573   stn->leg[0] = stn->leg[1] = stn->leg[2] = NULL;
574   add_stn_to_list(fixed ? &fixedlist : &stnlist, stn);
575   name->stn = stn;
576   // Don't re-count a station which already exists from before a `*solve`.
577   // After we solve we delete and NULL-out its `node*`, but set SFLAGS_SOLVED.
578   if (!TSTBIT(name->sflags, SFLAGS_SOLVED)) cStns++;
579   return stn;
580}
581
582extern void
583fprint_prefix(FILE *fh, const prefix *ptr)
584{
585   SVX_ASSERT(ptr);
586   if (TSTBIT(ptr->sflags, SFLAGS_ANON)) {
587      /* We release the stations, so ptr->stn is NULL late on, so we can't
588       * use that to print "anonymous station surveyed from somesurvey.12"
589       * here.  FIXME */
590      fputs("anonymous station", fh);
591      /* FIXME: if ident is set, show it? */
592      return;
593   }
594   if (ptr->up != NULL) {
595      fprint_prefix(fh, ptr->up);
596      if (ptr->up->up != NULL) fputc(output_separator, fh);
597      SVX_ASSERT(prefix_ident(ptr));
598      fputs(prefix_ident(ptr), fh);
599   }
600}
601
602static char *buffer = NULL;
603static OSSIZE_T buffer_len = 256;
604
605static OSSIZE_T
606sprint_prefix_(const prefix *ptr)
607{
608   OSSIZE_T len = 1;
609   if (ptr->up != NULL) {
610      const char *ident = prefix_ident(ptr);
611      SVX_ASSERT(ident);
612      len = sprint_prefix_(ptr->up);
613      OSSIZE_T end = len - 1;
614      if (ptr->up->up != NULL) len++;
615      len += strlen(ident);
616      if (len > buffer_len) {
617         buffer = osrealloc(buffer, len);
618         buffer_len = len;
619      }
620      char *p = buffer + end;
621      if (ptr->up->up != NULL) *p++ = output_separator;
622      strcpy(p, ident);
623   }
624   return len;
625}
626
627extern char *
628sprint_prefix(const prefix *ptr)
629{
630   SVX_ASSERT(ptr);
631   if (!buffer) buffer = osmalloc(buffer_len);
632   if (TSTBIT(ptr->sflags, SFLAGS_ANON)) {
633      /* We release the stations, so ptr->stn is NULL late on, so we can't
634       * use that to print "anonymous station surveyed from somesurvey.12"
635       * here.  FIXME */
636      strcpy(buffer, "anonymous station");
637      /* FIXME: if ident is set, show it? */
638      return buffer;
639   }
640   *buffer = '\0';
641   sprint_prefix_(ptr);
642   return buffer;
643}
644
645/* r = ab ; r,a,b are variance matrices */
646void
647mulss(var *r, const svar *a, const svar *b)
648{
649#ifdef NO_COVARIANCES
650   /* variance-only version */
651   (*r)[0] = (*a)[0] * (*b)[0];
652   (*r)[1] = (*a)[1] * (*b)[1];
653   (*r)[2] = (*a)[2] * (*b)[2];
654#else
655#if 0
656   SVX_ASSERT((const var *)r != a);
657   SVX_ASSERT((const var *)r != b);
658#endif
659
660   check_svar(a);
661   check_svar(b);
662
663   for (int i = 0; i < 3; i++) {
664      for (int j = 0; j < 3; j++) {
665         real tot = 0;
666         for (int k = 0; k < 3; k++) {
667            tot += SN(a,i,k) * SN(b,k,j);
668         }
669         (*r)[i][j] = tot;
670      }
671   }
672   check_var(r);
673#endif
674}
675
676#ifndef NO_COVARIANCES
677/* r = ab ; r,a,b are variance matrices */
678void
679smulvs(svar *r, const var *a, const svar *b)
680{
681#if 0
682   SVX_ASSERT((const var *)r != a);
683#endif
684   SVX_ASSERT((const svar *)r != b);
685
686   check_var(a);
687   check_svar(b);
688
689   (*r)[3]=(*r)[4]=(*r)[5]=-999;
690   for (int i = 0; i < 3; i++) {
691      for (int j = 0; j < 3; j++) {
692         real tot = 0;
693         for (int k = 0; k < 3; k++) {
694            tot += (*a)[i][k] * SN(b,k,j);
695         }
696         if (i <= j)
697            SN(r,i,j) = tot;
698         else if (fabs(SN(r,j,i) - tot) > THRESHOLD) {
699            printf("not sym - %d,%d = %f, %d,%d was %f\n",
700                   i,j,tot,j,i,SN(r,j,i));
701            BUG("smulvs didn't produce a sym mx\n");
702         }
703      }
704   }
705   check_svar(r);
706}
707#endif
708
709/* r = vb ; r,b delta vectors; a variance matrix */
710void
711mulsd(delta *r, const svar *v, const delta *b)
712{
713#ifdef NO_COVARIANCES
714   /* variance-only version */
715   (*r)[0] = (*v)[0] * (*b)[0];
716   (*r)[1] = (*v)[1] * (*b)[1];
717   (*r)[2] = (*v)[2] * (*b)[2];
718#else
719   SVX_ASSERT((const delta*)r != b);
720   check_svar(v);
721   check_d(b);
722
723   for (int i = 0; i < 3; i++) {
724      real tot = 0;
725      for (int j = 0; j < 3; j++) tot += S(i,j) * (*b)[j];
726      (*r)[i] = tot;
727   }
728   check_d(r);
729#endif
730}
731
732/* r = ca ; r,a variance matrices; c real scaling factor  */
733void
734mulsc(svar *r, const svar *a, real c)
735{
736#ifdef NO_COVARIANCES
737   /* variance-only version */
738   (*r)[0] = (*a)[0] * c;
739   (*r)[1] = (*a)[1] * c;
740   (*r)[2] = (*a)[2] * c;
741#else
742   check_svar(a);
743   for (int i = 0; i < 6; i++) (*r)[i] = (*a)[i] * c;
744   check_svar(r);
745#endif
746}
747
748/* r = a + b ; r,a,b delta vectors */
749void
750adddd(delta *r, const delta *a, const delta *b)
751{
752   check_d(a);
753   check_d(b);
754   (*r)[0] = (*a)[0] + (*b)[0];
755   (*r)[1] = (*a)[1] + (*b)[1];
756   (*r)[2] = (*a)[2] + (*b)[2];
757   check_d(r);
758}
759
760/* r = a - b ; r,a,b delta vectors */
761void
762subdd(delta *r, const delta *a, const delta *b) {
763   check_d(a);
764   check_d(b);
765   (*r)[0] = (*a)[0] - (*b)[0];
766   (*r)[1] = (*a)[1] - (*b)[1];
767   (*r)[2] = (*a)[2] - (*b)[2];
768   check_d(r);
769}
770
771/* r = a + b ; r,a,b variance matrices */
772void
773addss(svar *r, const svar *a, const svar *b)
774{
775#ifdef NO_COVARIANCES
776   /* variance-only version */
777   (*r)[0] = (*a)[0] + (*b)[0];
778   (*r)[1] = (*a)[1] + (*b)[1];
779   (*r)[2] = (*a)[2] + (*b)[2];
780#else
781   check_svar(a);
782   check_svar(b);
783   for (int i = 0; i < 6; i++) (*r)[i] = (*a)[i] + (*b)[i];
784   check_svar(r);
785#endif
786}
787
788/* r = a - b ; r,a,b variance matrices */
789void
790subss(svar *r, const svar *a, const svar *b)
791{
792#ifdef NO_COVARIANCES
793   /* variance-only version */
794   (*r)[0] = (*a)[0] - (*b)[0];
795   (*r)[1] = (*a)[1] - (*b)[1];
796   (*r)[2] = (*a)[2] - (*b)[2];
797#else
798   check_svar(a);
799   check_svar(b);
800   for (int i = 0; i < 6; i++) (*r)[i] = (*a)[i] - (*b)[i];
801   check_svar(r);
802#endif
803}
804
805/* inv = v^-1 ; inv,v variance matrices */
806extern int
807invert_svar(svar *inv, const svar *v)
808{
809#ifdef NO_COVARIANCES
810   for (int i = 0; i < 3; i++) {
811      if ((*v)[i] == 0.0) return 0; /* matrix is singular */
812      (*inv)[i] = 1.0 / (*v)[i];
813   }
814#else
815#if 0
816   SVX_ASSERT((const var *)inv != v);
817#endif
818
819   check_svar(v);
820   /* a d e
821    * d b f
822    * e f c
823    */
824   real a = (*v)[0], b = (*v)[1], c = (*v)[2];
825   real d = (*v)[3], e = (*v)[4], f = (*v)[5];
826   real bcff = b * c - f * f;
827   real efcd = e * f - c * d;
828   real dfbe = d * f - b * e;
829   real det = a * bcff + d * efcd + e * dfbe;
830
831   if (det == 0.0) {
832      /* printf("det=%.20f\n", det); */
833      return 0; /* matrix is singular */
834   }
835
836   det = 1 / det;
837
838   (*inv)[0] = det * bcff;
839   (*inv)[1] = det * (c * a - e * e);
840   (*inv)[2] = det * (a * b - d * d);
841   (*inv)[3] = det * efcd;
842   (*inv)[4] = det * dfbe;
843   (*inv)[5] = det * (e * d - a * f);
844
845#if 0
846   /* This test fires very occasionally, and there's not much point in
847    * it anyhow - the matrix inversion algorithm is simple enough that
848    * we can be confident it's correctly implemented, so we might as
849    * well save the cycles and not perform this check.
850    */
851     { /* check that original * inverse = identity matrix */
852        int i;
853        var p;
854        real D = 0;
855        mulss(&p, v, inv);
856        for (i = 0; i < 3; i++) {
857           int j;
858           for (j = 0; j < 3; j++) D += fabs(p[i][j] - (real)(i==j));
859        }
860        if (D > THRESHOLD) {
861           printf("original * inverse=\n");
862           print_svar(*v);
863           printf("*\n");
864           print_svar(*inv);
865           printf("=\n");
866           print_var(p);
867           BUG("matrix didn't invert");
868        }
869        check_svar(inv);
870     }
871#endif
872#endif
873   return 1;
874}
875
876/* r = (b^-1)a ; r,a delta vectors; b variance matrix */
877#ifndef NO_COVARIANCES
878void
879divds(delta *r, const delta *a, const svar *b)
880{
881#ifdef NO_COVARIANCES
882   /* variance-only version */
883   (*r)[0] = (*a)[0] / (*b)[0];
884   (*r)[1] = (*a)[1] / (*b)[1];
885   (*r)[2] = (*a)[2] / (*b)[2];
886#else
887   svar b_inv;
888   if (!invert_svar(&b_inv, b)) {
889      print_svar(*b);
890      BUG("covariance matrix is singular");
891   }
892   mulsd(r, &b_inv, a);
893#endif
894}
895#endif
896
897bool
898fZeros(const svar *v) {
899#ifdef NO_COVARIANCES
900   /* variance-only version */
901   return ((*v)[0] == 0.0 && (*v)[1] == 0.0 && (*v)[2] == 0.0);
902#else
903   check_svar(v);
904   for (int i = 0; i < 6; i++) if ((*v)[i] != 0.0) return false;
905
906   return true;
907#endif
908}
Note: See TracBrowser for help on using the repository browser.