source: git/src/netbits.c @ 62be3ee

stereo-2025
Last change on this file since 62be3ee was a49a80c0, checked in by Olly Betts <olly@…>, 4 months ago

Clean up inclusion of osalloc.h

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