source: git/src/netbits.c @ 2b5c6b3

stereo-2025
Last change on this file since 2b5c6b3 was 8676839, checked in by Olly Betts <olly@…>, 8 months ago

Eliminate unnecessary struct LinkRev?

Just use linkcommon directly for the reverse leg.

  • 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(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   ++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(linkcommon);
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.