source: git/src/netbits.c @ 5a2d346

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

Fix component counting bugs

The component count was wrong in some cases, and we calculate the number
of loops using this component count, so the loop count would be wrong by
the same amount in these cases.

  • Property mode set to 100644
File size: 22.5 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(linkrev);
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   ++fr->name->shape;
305   ++to->name->shape;
306
307   return leg;
308}
309
310/* Add a leg between names *fr_name and *to_name
311 * If either is a three node, then it is split into two
312 * and the data structure adjusted as necessary.
313 */
314void
315addlegbyname(prefix *fr_name, prefix *to_name, bool fToFirst,
316             real dx, real dy, real dz,
317             real vx, real vy, real vz
318#ifndef NO_COVARIANCES
319             , real cyz, real czx, real cxy
320#endif
321             )
322{
323   if (to_name == fr_name) {
324      int type = pcs->from_equals_to_is_only_a_warning ? DIAG_WARN : DIAG_ERR;
325      /* TRANSLATORS: Here a "survey leg" is a set of measurements between two
326       * "survey stations".
327       *
328       * %s is replaced by the name of the station. */
329      compile_diagnostic(type, /*Survey leg with same station (“%s”) at both ends - typing error?*/50,
330                         sprint_prefix(to_name));
331      return;
332   }
333   node *to, *fr;
334   if (fToFirst) {
335      to = StnFromPfx(to_name);
336      fr = StnFromPfx(fr_name);
337   } else {
338      fr = StnFromPfx(fr_name);
339      to = StnFromPfx(to_name);
340   }
341   if (last_leg.to_name) {
342      if (last_leg.to_name == to_name && last_leg.fr_name == fr_name) {
343         /* FIXME: Not the right way to average... */
344         linkfor * leg = last_leg.leg;
345         int n = last_leg.n++;
346         leg->d[0] = (leg->d[0] * n + dx) / (n + 1);
347         leg->d[1] = (leg->d[1] * n + dy) / (n + 1);
348         leg->d[2] = (leg->d[2] * n + dz) / (n + 1);
349#ifndef NO_COVARIANCES
350         leg->v[0] = (leg->v[0] * n + vx) / (n + 1);
351         leg->v[1] = (leg->v[1] * n + vy) / (n + 1);
352         leg->v[2] = (leg->v[2] * n + vz) / (n + 1);
353         leg->v[3] = (leg->v[3] * n + cxy) / (n + 1);
354         leg->v[4] = (leg->v[4] * n + czx) / (n + 1);
355         leg->v[5] = (leg->v[5] * n + cyz) / (n + 1);
356         check_svar(&(leg->v));
357#else
358         leg->v[0] = (leg->v[0] * n + vx) / (n + 1);
359         leg->v[1] = (leg->v[1] * n + vy) / (n + 1);
360         leg->v[2] = (leg->v[2] * n + vz) / (n + 1);
361#endif
362         return;
363      }
364   }
365   cLegs++;
366
367   last_leg.to_name = to_name;
368   last_leg.fr_name = fr_name;
369   last_leg.n = 1;
370   last_leg.leg = addleg_(fr, to, dx, dy, dz, vx, vy, vz,
371#ifndef NO_COVARIANCES
372                          cyz, czx, cxy,
373#endif
374                          0);
375}
376
377/* helper function for replace_pfx */
378static void
379replace_pfx_(node *stn, node *from, pos *pos_with, bool move_to_fixedlist)
380{
381   SVX_ASSERT(!fixed(stn));
382   if (move_to_fixedlist) {
383      SVX_ASSERT(pos_fixed(pos_with));
384      SVX_ASSERT(!fixed(stn));
385      remove_stn_from_list(&stnlist, stn);
386      add_stn_to_list(&fixedlist, stn);
387   }
388   stn->name->pos = pos_with;
389   for (int d = 0; d < 3; d++) {
390      linkfor *leg = stn->leg[d];
391      if (!leg) break;
392      node *to = leg->l.to;
393      if (to == from) continue;
394
395      if (fZeros(data_here(leg) ? &leg->v : &reverse_leg(leg)->v))
396         replace_pfx_(to, stn, pos_with, move_to_fixedlist);
397   }
398}
399
400/* We used to iterate over the whole station list (inefficient) - now we
401 * just look at any neighbouring nodes to see if they are equated */
402static void
403replace_pfx(const prefix *pfx_replace, const prefix *pfx_with,
404            bool move_to_fixedlist)
405{
406   SVX_ASSERT(pfx_replace);
407   SVX_ASSERT(pfx_with);
408   pos *pos_replace = pfx_replace->pos;
409   SVX_ASSERT(pos_replace != pfx_with->pos);
410
411   replace_pfx_(pfx_replace->stn, NULL, pfx_with->pos, move_to_fixedlist);
412
413#if DEBUG_INVALID
414   for (node *stn = stnlist; stn; stn = stn->next) {
415      SVX_ASSERT(stn->name->pos != pos_replace);
416   }
417   for (node *stn = fixedlist; stn; stn = stn->next) {
418      SVX_ASSERT(stn->name->pos != pos_replace);
419   }
420#endif
421
422   /* free the (now-unused) old pos */
423   osfree(pos_replace);
424}
425
426// Add equating leg between existing stations whose names are name1 and name2.
427void
428process_equate(prefix *name1, prefix *name2)
429{
430   clear_last_leg();
431   if (name1 == name2) {
432      /* catch something like *equate "fred fred" */
433      /* TRANSLATORS: Here "station" is a survey station, not a train station.
434       */
435      compile_diagnostic(DIAG_WARN, /*Station “%s” equated to itself*/13,
436                         sprint_prefix(name1));
437      return;
438   }
439   node *stn1 = StnFromPfx(name1);
440   node *stn2 = StnFromPfx(name2);
441   /* equate nodes if not already equated */
442   if (name1->pos != name2->pos) {
443      if (pfx_fixed(name1)) {
444         bool name2_fixed = pfx_fixed(name2);
445         if (name2_fixed) {
446            /* both are fixed, but let them off iff their coordinates match */
447            char *s = osstrdup(sprint_prefix(name1));
448            for (int d = 2; d >= 0; d--) {
449               if (name1->pos->p[d] != name2->pos->p[d]) {
450                  compile_diagnostic(DIAG_ERR, /*Tried to equate two non-equal fixed stations: “%s” and “%s”*/52,
451                                     s, sprint_prefix(name2));
452                  osfree(s);
453                  return;
454               }
455            }
456            /* TRANSLATORS: "equal" as in:
457             *
458             * *fix a 1 2 3
459             * *fix b 1 2 3
460             * *equate a b */
461            compile_diagnostic(DIAG_WARN, /*Equating two equal fixed points: “%s” and “%s”*/53,
462                               s, sprint_prefix(name2));
463            osfree(s);
464         }
465
466         /* name1 is fixed, so replace all refs to name2's pos with name1's */
467         replace_pfx(name2, name1, !name2_fixed);
468      } else {
469         /* name1 isn't fixed, so replace all refs to its pos with name2's */
470         replace_pfx(name1, name2, pfx_fixed(name2));
471      }
472
473      /* count equates as legs for now... */
474      cLegs++;
475      addleg_(stn1, stn2,
476              (real)0.0, (real)0.0, (real)0.0,
477              (real)0.0, (real)0.0, (real)0.0,
478#ifndef NO_COVARIANCES
479              (real)0.0, (real)0.0, (real)0.0,
480#endif
481              FLAG_FAKE);
482   }
483}
484
485/* Add a 'fake' leg (not counted) between existing stations *fr and *to
486 * (which *must* be different)
487 * If either node is a three node, then it is split into two
488 * and the data structure adjusted as necessary
489 */
490void
491addfakeleg(node *fr, node *to,
492           real dx, real dy, real dz,
493           real vx, real vy, real vz
494#ifndef NO_COVARIANCES
495           , real cyz, real czx, real cxy
496#endif
497           )
498{
499   clear_last_leg();
500   addleg_(fr, to, dx, dy, dz, vx, vy, vz,
501#ifndef NO_COVARIANCES
502           cyz, czx, cxy,
503#endif
504           FLAG_FAKE);
505}
506
507static char
508freeleg(node **stnptr)
509{
510   node *stn = *stnptr;
511
512   if (stn->leg[0] == NULL) return 0; /* leg[0] unused */
513   if (stn->leg[1] == NULL) return 1; /* leg[1] unused */
514   if (stn->leg[2] == NULL) return 2; /* leg[2] unused */
515
516   /* All legs used, so split node in two */
517   node *newstn = osnew(node);
518   linkfor *leg = osnew(linkfor);
519   linkfor *leg2 = (linkfor*)osnew(linkrev);
520
521   *stnptr = newstn;
522
523   add_stn_to_list(fixed(stn) ? &fixedlist : &stnlist, newstn);
524   newstn->name = stn->name;
525
526   leg->l.to = newstn;
527   leg->d[0] = leg->d[1] = leg->d[2] = (real)0.0;
528
529#ifndef NO_COVARIANCES
530   for (int i = 0; i < 6; i++) leg->v[i] = (real)0.0;
531#else
532   leg->v[0] = leg->v[1] = leg->v[2] = (real)0.0;
533#endif
534   leg->l.reverse = 1 | FLAG_DATAHERE | FLAG_FAKE;
535   leg->l.flags = pcs->flags | (pcs->recorded_style << FLAGS_STYLE_BIT0);
536
537   leg2->l.to = stn;
538   leg2->l.reverse = 0;
539
540   // NB this preserves pos->stn->leg[0] pointing to the "real" fixed point
541   // for stations fixed with error estimates.
542   newstn->leg[0] = stn->leg[0];
543   // Update the reverse leg.
544   reverse_leg(newstn->leg[0])->l.to = newstn;
545   newstn->leg[1] = leg2;
546
547   stn->leg[0] = leg;
548
549   newstn->leg[2] = NULL; /* needed as newstn->leg[dirn]==NULL indicates unused */
550
551   return 2; /* leg[2] unused */
552}
553
554node *
555StnFromPfx(prefix *name)
556{
557   if (name->stn != NULL) return name->stn;
558   node *stn = osnew(node);
559   stn->name = name;
560   bool fixed = false;
561   if (name->pos == NULL) {
562      name->pos = osnew(pos);
563      unfix(stn);
564   } else {
565      fixed = pfx_fixed(name);
566   }
567   stn->leg[0] = stn->leg[1] = stn->leg[2] = NULL;
568   add_stn_to_list(fixed ? &fixedlist : &stnlist, stn);
569   name->stn = stn;
570   cStns++;
571   return stn;
572}
573
574extern void
575fprint_prefix(FILE *fh, const prefix *ptr)
576{
577   SVX_ASSERT(ptr);
578   if (TSTBIT(ptr->sflags, SFLAGS_ANON)) {
579      /* We release the stations, so ptr->stn is NULL late on, so we can't
580       * use that to print "anonymous station surveyed from somesurvey.12"
581       * here.  FIXME */
582      fputs("anonymous station", fh);
583      /* FIXME: if ident is set, show it? */
584      return;
585   }
586   if (ptr->up != NULL) {
587      fprint_prefix(fh, ptr->up);
588      if (ptr->up->up != NULL) fputc(output_separator, fh);
589      SVX_ASSERT(prefix_ident(ptr));
590      fputs(prefix_ident(ptr), fh);
591   }
592}
593
594static char *buffer = NULL;
595static OSSIZE_T buffer_len = 256;
596
597static OSSIZE_T
598sprint_prefix_(const prefix *ptr)
599{
600   OSSIZE_T len = 1;
601   if (ptr->up != NULL) {
602      const char *ident = prefix_ident(ptr);
603      SVX_ASSERT(ident);
604      len = sprint_prefix_(ptr->up);
605      OSSIZE_T end = len - 1;
606      if (ptr->up->up != NULL) len++;
607      len += strlen(ident);
608      if (len > buffer_len) {
609         buffer = osrealloc(buffer, len);
610         buffer_len = len;
611      }
612      char *p = buffer + end;
613      if (ptr->up->up != NULL) *p++ = output_separator;
614      strcpy(p, ident);
615   }
616   return len;
617}
618
619extern char *
620sprint_prefix(const prefix *ptr)
621{
622   SVX_ASSERT(ptr);
623   if (!buffer) buffer = osmalloc(buffer_len);
624   if (TSTBIT(ptr->sflags, SFLAGS_ANON)) {
625      /* We release the stations, so ptr->stn is NULL late on, so we can't
626       * use that to print "anonymous station surveyed from somesurvey.12"
627       * here.  FIXME */
628      strcpy(buffer, "anonymous station");
629      /* FIXME: if ident is set, show it? */
630      return buffer;
631   }
632   *buffer = '\0';
633   sprint_prefix_(ptr);
634   return buffer;
635}
636
637/* r = ab ; r,a,b are variance matrices */
638void
639mulss(var *r, const svar *a, const svar *b)
640{
641#ifdef NO_COVARIANCES
642   /* variance-only version */
643   (*r)[0] = (*a)[0] * (*b)[0];
644   (*r)[1] = (*a)[1] * (*b)[1];
645   (*r)[2] = (*a)[2] * (*b)[2];
646#else
647#if 0
648   SVX_ASSERT((const var *)r != a);
649   SVX_ASSERT((const var *)r != b);
650#endif
651
652   check_svar(a);
653   check_svar(b);
654
655   for (int i = 0; i < 3; i++) {
656      for (int j = 0; j < 3; j++) {
657         real tot = 0;
658         for (int k = 0; k < 3; k++) {
659            tot += SN(a,i,k) * SN(b,k,j);
660         }
661         (*r)[i][j] = tot;
662      }
663   }
664   check_var(r);
665#endif
666}
667
668#ifndef NO_COVARIANCES
669/* r = ab ; r,a,b are variance matrices */
670void
671smulvs(svar *r, const var *a, const svar *b)
672{
673#if 0
674   SVX_ASSERT((const var *)r != a);
675#endif
676   SVX_ASSERT((const svar *)r != b);
677
678   check_var(a);
679   check_svar(b);
680
681   (*r)[3]=(*r)[4]=(*r)[5]=-999;
682   for (int i = 0; i < 3; i++) {
683      for (int j = 0; j < 3; j++) {
684         real tot = 0;
685         for (int k = 0; k < 3; k++) {
686            tot += (*a)[i][k] * SN(b,k,j);
687         }
688         if (i <= j)
689            SN(r,i,j) = tot;
690         else if (fabs(SN(r,j,i) - tot) > THRESHOLD) {
691            printf("not sym - %d,%d = %f, %d,%d was %f\n",
692                   i,j,tot,j,i,SN(r,j,i));
693            BUG("smulvs didn't produce a sym mx\n");
694         }
695      }
696   }
697   check_svar(r);
698}
699#endif
700
701/* r = vb ; r,b delta vectors; a variance matrix */
702void
703mulsd(delta *r, const svar *v, const delta *b)
704{
705#ifdef NO_COVARIANCES
706   /* variance-only version */
707   (*r)[0] = (*v)[0] * (*b)[0];
708   (*r)[1] = (*v)[1] * (*b)[1];
709   (*r)[2] = (*v)[2] * (*b)[2];
710#else
711   SVX_ASSERT((const delta*)r != b);
712   check_svar(v);
713   check_d(b);
714
715   for (int i = 0; i < 3; i++) {
716      real tot = 0;
717      for (int j = 0; j < 3; j++) tot += S(i,j) * (*b)[j];
718      (*r)[i] = tot;
719   }
720   check_d(r);
721#endif
722}
723
724/* r = ca ; r,a variance matrices; c real scaling factor  */
725void
726mulsc(svar *r, const svar *a, real c)
727{
728#ifdef NO_COVARIANCES
729   /* variance-only version */
730   (*r)[0] = (*a)[0] * c;
731   (*r)[1] = (*a)[1] * c;
732   (*r)[2] = (*a)[2] * c;
733#else
734   check_svar(a);
735   for (int i = 0; i < 6; i++) (*r)[i] = (*a)[i] * c;
736   check_svar(r);
737#endif
738}
739
740/* r = a + b ; r,a,b delta vectors */
741void
742adddd(delta *r, const delta *a, const delta *b)
743{
744   check_d(a);
745   check_d(b);
746   (*r)[0] = (*a)[0] + (*b)[0];
747   (*r)[1] = (*a)[1] + (*b)[1];
748   (*r)[2] = (*a)[2] + (*b)[2];
749   check_d(r);
750}
751
752/* r = a - b ; r,a,b delta vectors */
753void
754subdd(delta *r, const delta *a, const delta *b) {
755   check_d(a);
756   check_d(b);
757   (*r)[0] = (*a)[0] - (*b)[0];
758   (*r)[1] = (*a)[1] - (*b)[1];
759   (*r)[2] = (*a)[2] - (*b)[2];
760   check_d(r);
761}
762
763/* r = a + b ; r,a,b variance matrices */
764void
765addss(svar *r, const svar *a, const svar *b)
766{
767#ifdef NO_COVARIANCES
768   /* variance-only version */
769   (*r)[0] = (*a)[0] + (*b)[0];
770   (*r)[1] = (*a)[1] + (*b)[1];
771   (*r)[2] = (*a)[2] + (*b)[2];
772#else
773   check_svar(a);
774   check_svar(b);
775   for (int i = 0; i < 6; i++) (*r)[i] = (*a)[i] + (*b)[i];
776   check_svar(r);
777#endif
778}
779
780/* r = a - b ; r,a,b variance matrices */
781void
782subss(svar *r, const svar *a, const svar *b)
783{
784#ifdef NO_COVARIANCES
785   /* variance-only version */
786   (*r)[0] = (*a)[0] - (*b)[0];
787   (*r)[1] = (*a)[1] - (*b)[1];
788   (*r)[2] = (*a)[2] - (*b)[2];
789#else
790   check_svar(a);
791   check_svar(b);
792   for (int i = 0; i < 6; i++) (*r)[i] = (*a)[i] - (*b)[i];
793   check_svar(r);
794#endif
795}
796
797/* inv = v^-1 ; inv,v variance matrices */
798extern int
799invert_svar(svar *inv, const svar *v)
800{
801#ifdef NO_COVARIANCES
802   for (int i = 0; i < 3; i++) {
803      if ((*v)[i] == 0.0) return 0; /* matrix is singular */
804      (*inv)[i] = 1.0 / (*v)[i];
805   }
806#else
807#if 0
808   SVX_ASSERT((const var *)inv != v);
809#endif
810
811   check_svar(v);
812   /* a d e
813    * d b f
814    * e f c
815    */
816   real a = (*v)[0], b = (*v)[1], c = (*v)[2];
817   real d = (*v)[3], e = (*v)[4], f = (*v)[5];
818   real bcff = b * c - f * f;
819   real efcd = e * f - c * d;
820   real dfbe = d * f - b * e;
821   real det = a * bcff + d * efcd + e * dfbe;
822
823   if (det == 0.0) {
824      /* printf("det=%.20f\n", det); */
825      return 0; /* matrix is singular */
826   }
827
828   det = 1 / det;
829
830   (*inv)[0] = det * bcff;
831   (*inv)[1] = det * (c * a - e * e);
832   (*inv)[2] = det * (a * b - d * d);
833   (*inv)[3] = det * efcd;
834   (*inv)[4] = det * dfbe;
835   (*inv)[5] = det * (e * d - a * f);
836
837#if 0
838   /* This test fires very occasionally, and there's not much point in
839    * it anyhow - the matrix inversion algorithm is simple enough that
840    * we can be confident it's correctly implemented, so we might as
841    * well save the cycles and not perform this check.
842    */
843     { /* check that original * inverse = identity matrix */
844        int i;
845        var p;
846        real D = 0;
847        mulss(&p, v, inv);
848        for (i = 0; i < 3; i++) {
849           int j;
850           for (j = 0; j < 3; j++) D += fabs(p[i][j] - (real)(i==j));
851        }
852        if (D > THRESHOLD) {
853           printf("original * inverse=\n");
854           print_svar(*v);
855           printf("*\n");
856           print_svar(*inv);
857           printf("=\n");
858           print_var(p);
859           BUG("matrix didn't invert");
860        }
861        check_svar(inv);
862     }
863#endif
864#endif
865   return 1;
866}
867
868/* r = (b^-1)a ; r,a delta vectors; b variance matrix */
869#ifndef NO_COVARIANCES
870void
871divds(delta *r, const delta *a, const svar *b)
872{
873#ifdef NO_COVARIANCES
874   /* variance-only version */
875   (*r)[0] = (*a)[0] / (*b)[0];
876   (*r)[1] = (*a)[1] / (*b)[1];
877   (*r)[2] = (*a)[2] / (*b)[2];
878#else
879   svar b_inv;
880   if (!invert_svar(&b_inv, b)) {
881      print_svar(*b);
882      BUG("covariance matrix is singular");
883   }
884   mulsd(r, &b_inv, a);
885#endif
886}
887#endif
888
889bool
890fZeros(const svar *v) {
891#ifdef NO_COVARIANCES
892   /* variance-only version */
893   return ((*v)[0] == 0.0 && (*v)[1] == 0.0 && (*v)[2] == 0.0);
894#else
895   check_svar(v);
896   for (int i = 0; i < 6; i++) if ((*v)[i] != 0.0) return false;
897
898   return true;
899#endif
900}
Note: See TracBrowser for help on using the repository browser.