#ifndef lint
static const char RCSid[] = "$Id: pmapcontrib.c,v 2.19 2018/11/08 00:54:07 greg Exp $";
#endif

/* 
   ======================================================================
   Photon map for light source contributions

   Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
   (c) Lucerne University of Applied Sciences and Arts,
       supported by the Swiss National Science Foundation (SNSF, #147053)
   ======================================================================
   
   $Id: pmapcontrib.c,v 2.19 2018/11/08 00:54:07 greg Exp $
*/


#include "pmapcontrib.h"
#include "pmapmat.h"
#include "pmapsrc.h"
#include "pmaprand.h"
#include "pmapio.h"
#include "pmapdiag.h"
#include "rcontrib.h"
#include "otypes.h"
#include "otspecial.h"
#if NIX
   #include <sys/mman.h>
   #include <sys/wait.h>   
#endif


static PhotonPrimaryIdx newPhotonPrimary (PhotonMap *pmap, 
                                          const RAY *primRay, 
                                          FILE *primHeap)
/* Add primary ray for emitted photon and save light source index, origin on
 * source, and emitted direction; used by contrib photons. The current
 * primary is stored in pmap -> lastPrimary.  If the previous primary
 * contributed photons (has srcIdx >= 0), it's appended to primHeap.  If
 * primRay == NULL, the current primary is still flushed, but no new primary
 * is set.  Returns updated primary counter pmap -> numPrimary.  */
{
   if (!pmap || !primHeap)
      return 0;
      
   /* Check if last primary ray has spawned photons (srcIdx >= 0, see
    * newPhoton()), in which case we save it to the primary heap file
    * before clobbering it */
   if (pmap -> lastPrimary.srcIdx >= 0) {
      if (!fwrite(&pmap -> lastPrimary, sizeof(PhotonPrimary), 1, primHeap))
         error(SYSTEM, "failed writing photon primary in newPhotonPrimary");
         
      pmap -> numPrimary++;
      if (pmap -> numPrimary > PMAP_MAXPRIMARY)
         error(INTERNAL, "photon primary overflow in newPhotonPrimary");
   }

   /* Mark unused with negative source index until path spawns a photon (see
    * newPhoton()) */
   pmap -> lastPrimary.srcIdx = -1;
     
   if (primRay) { 
      FVECT dvec;

#ifdef PMAP_PRIMARYDIR            
      /* Reverse incident direction to point to light source */
      dvec [0] = -primRay -> rdir [0];
      dvec [1] = -primRay -> rdir [1];
      dvec [2] = -primRay -> rdir [2];
      pmap -> lastPrimary.dir = encodedir(dvec);
#endif      
#ifdef PMAP_PRIMARYPOS      
      VCOPY(pmap -> lastPrimary.pos, primRay -> rop);
#endif      
   }
   
   return pmap -> numPrimary;
}



#ifdef DEBUG_PMAP
static int checkPrimaryHeap (FILE *file)
/* Check heap for ordered primaries */
{
   Photon   p, lastp;
   int      i, dup;
   
   rewind(file);
   memset(&lastp, 0, sizeof(lastp));
   
   while (fread(&p, sizeof(p), 1, file)) {
      dup = 1;
      
      for (i = 0; i <= 2; i++) {
         if (p.pos [i] < thescene.cuorg [i] || 
             p.pos [i] > thescene.cuorg [i] + thescene.cusize) { 
             
            sprintf(errmsg, "corrupt photon in heap at [%f, %f, %f]\n", 
                    p.pos [0], p.pos [1], p.pos [2]);
            error(WARNING, errmsg);
         }
         
         dup &= p.pos [i] == lastp.pos [i];
      }
      
      if (dup) {
         sprintf(errmsg,
                 "consecutive duplicate photon in heap at [%f, %f, %f]\n", 
                 p.pos [0], p.pos [1], p.pos [2]);
         error(WARNING, errmsg);
      }
   }

   return 0;
}
#endif



static PhotonPrimaryIdx buildPrimaries (PhotonMap *pmap, FILE **primaryHeap,
                                        char **primaryHeapFname,
                                        PhotonPrimaryIdx *primaryOfs, 
                                        unsigned numHeaps)
/* Consolidate per-subprocess photon primary heaps into the primary array
 * pmap -> primaries. Returns offset for primary index linearisation in
 * numPrimary. The heap files in primaryHeap are closed on return. */
{
   PhotonPrimaryIdx  heapLen;
   unsigned          heap;
   
   if (!pmap || !primaryHeap || !primaryOfs || !numHeaps)
      return 0;
      
   pmap -> numPrimary = 0;
   
   for (heap = 0; heap < numHeaps; heap++) {
      primaryOfs [heap] = pmap -> numPrimary;
      
      if (fseek(primaryHeap [heap], 0, SEEK_END) < 0)
         error(SYSTEM, "failed photon primary seek in buildPrimaries");
      pmap -> numPrimary += heapLen = ftell(primaryHeap [heap]) / 
                                      sizeof(PhotonPrimary);      

      pmap -> primaries = realloc(pmap -> primaries, 
                                  pmap -> numPrimary * 
                                  sizeof(PhotonPrimary));  
      if (!pmap -> primaries)
         error(SYSTEM, "failed photon primary alloc in buildPrimaries");

      rewind(primaryHeap [heap]);
      if (fread(pmap -> primaries + primaryOfs [heap], sizeof(PhotonPrimary),
                heapLen, primaryHeap [heap]) != heapLen)
         error(SYSTEM, "failed reading photon primaries in buildPrimaries");
      
      fclose(primaryHeap [heap]);
      unlink(primaryHeapFname [heap]);
   }
   
   return pmap -> numPrimary;
}      



/* Defs for photon emission counter array passed by sub-processes to parent
 * via shared memory */
typedef  unsigned long  PhotonContribCnt;

/* Indices for photon emission counter array: num photons stored and num
 * emitted per source */
#define  PHOTONCNT_NUMPHOT    0
#define  PHOTONCNT_NUMEMIT(n) (1 + n)






void distribPhotonContrib (PhotonMap* pm, unsigned numProc)
{
   EmissionMap       emap;
   char              errmsg2 [128], shmFname [PMAP_TMPFNLEN];
   unsigned          srcIdx, proc;
   int               shmFile, stat, pid;
   double            *srcFlux,         /* Emitted flux per light source */
                     srcDistribTarget; /* Target photon count per source */
   PhotonContribCnt  *photonCnt;       /* Photon emission counter array */
   unsigned          photonCntSize = sizeof(PhotonContribCnt) * 
                                     PHOTONCNT_NUMEMIT(nsources);
   FILE              **primaryHeap = NULL;
   char              **primaryHeapFname = NULL;
   PhotonPrimaryIdx  *primaryOfs = NULL;
                                    
   if (!pm)
      error(USER, "no photon map defined in distribPhotonContrib");
      
   if (!nsources)
      error(USER, "no light sources in distribPhotonContrib");

   if (nsources > MAXMODLIST)
      error(USER, "too many light sources in distribPhotonContrib");
      
   /* Allocate photon flux per light source; this differs for every 
    * source as all sources contribute the same number of distributed
    * photons (srcDistribTarget), hence the number of photons emitted per
    * source does not correlate with its emitted flux. The resulting flux
    * per photon is therefore adjusted individually for each source. */
   if (!(srcFlux = calloc(nsources, sizeof(double))))
      error(SYSTEM, "can't allocate source flux in distribPhotonContrib");

   /* ===================================================================
    * INITIALISATION - Set up emission and scattering funcs
    * =================================================================== */
   emap.samples = NULL;
   emap.src = NULL;
   emap.maxPartitions = MAXSPART;
   emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
   if (!emap.partitions)
      error(USER, "can't allocate source partitions in distribPhotonContrib");

   /* Initialise contrib photon map */   
   initPhotonMap(pm, PMAP_TYPE_CONTRIB);
   initPhotonHeap(pm);
   initPhotonEmissionFuncs();
   initPhotonScatterFuncs();
   
   /* Per-subprocess / per-source target counts */
   pm -> distribTarget /= numProc;
   srcDistribTarget = nsources ? (double)pm -> distribTarget / nsources : 0;   
   
   if (!pm -> distribTarget)
      error(INTERNAL, "no photons to distribute in distribPhotonContrib");
   
   /* Get photon ports from modifier list */
   getPhotonPorts(photonPortList);
      
   /* Get photon sensor modifiers */
   getPhotonSensors(photonSensorList);      

#if NIX   
   /* Set up shared mem for photon counters (zeroed by ftruncate) */
   strcpy(shmFname, PMAP_TMPFNAME);
   shmFile = mkstemp(shmFname);
   
   if (shmFile < 0 || ftruncate(shmFile, photonCntSize) < 0)
      error(SYSTEM, "failed shared mem init in distribPhotonContrib");

   photonCnt = mmap(NULL, photonCntSize, PROT_READ | PROT_WRITE, 
                    MAP_SHARED, shmFile, 0);
                     
   if (photonCnt == MAP_FAILED)
      error(SYSTEM, "failed shared mem mapping in distribPhotonContrib");
#else
   /* Allocate photon counters statically on Windoze */
   if (!(photonCnt = malloc(photonCntSize)))
      error(SYSTEM, "failed trivial malloc in distribPhotonContrib");
   
   for (srcIdx = 0; srcIdx < PHOTONCNT_NUMEMIT(nsources); srcIdx++)
      photonCnt [srcIdx] = 0;
#endif /* NIX */

   if (verbose) {
      sprintf(errmsg, "\nIntegrating flux from %d sources", nsources);

      if (photonPorts) {
         sprintf(errmsg2, " via %d ports", numPhotonPorts);
         strcat(errmsg, errmsg2);
      }

      strcat(errmsg, "\n");
      eputs(errmsg);
   }

   /* =============================================================
    * FLUX INTEGRATION - Get total flux emitted from sources/ports
    * ============================================================= */   
   for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
      unsigned portCnt = 0;      
      srcFlux [srcIdx] = 0;
      emap.src = source + srcIdx;
      
      do {  /* Need at least one iteration if no ports! */      
         emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt 
                                                   : NULL;
         photonPartition [emap.src -> so -> otype] (&emap);

         if (verbose) {
            sprintf(errmsg, "\tIntegrating flux from source %s ",
                    source [srcIdx].so -> oname);

            if (emap.port) {
               sprintf(errmsg2, "via port %s ", 
                       photonPorts [portCnt].so -> oname);
               strcat(errmsg, errmsg2);
            }

            sprintf(errmsg2, "(%lu partitions)\n", emap.numPartitions);
            strcat(errmsg, errmsg2);
            eputs(errmsg);
#if NIX            
            fflush(stderr);
#endif            
         }                    
         
         for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
              emap.partitionCnt++) {
            initPhotonEmission(&emap, pdfSamples);
            srcFlux [srcIdx] += colorAvg(emap.partFlux);
         }
         
         portCnt++;
      } while (portCnt < numPhotonPorts);         
      
      if (srcFlux [srcIdx] < FTINY) {
         sprintf(errmsg, "source %s has zero emission", 
                 source [srcIdx].so -> oname);
         error(WARNING, errmsg);
      }
   }   
   
   /* Allocate & init per-subprocess primary heap files */
   primaryHeap = calloc(numProc, sizeof(FILE*));
   primaryHeapFname = calloc(numProc, sizeof(char*));
   primaryOfs = calloc(numProc, sizeof(PhotonPrimaryIdx));
   if (!primaryHeap || !primaryHeapFname || !primaryOfs)
      error(SYSTEM, "failed primary heap allocation in "
            "distribPhotonContrib");
      
   for (proc = 0; proc < numProc; proc++) {
      primaryHeapFname [proc] = malloc(PMAP_TMPFNLEN);
      if (!primaryHeapFname [proc])
         error(SYSTEM, "failed primary heap file allocation in "
               "distribPhotonContrib");
               
      mktemp(strcpy(primaryHeapFname [proc], PMAP_TMPFNAME));
      if (!(primaryHeap [proc] = fopen(primaryHeapFname [proc], "w+b")))
         error(SYSTEM, "failed opening primary heap file in "
               "distribPhotonContrib");
   }               

   /* Record start time for progress reports */
   repStartTime = time(NULL);

   if (verbose) {
      sprintf(errmsg, "\nPhoton distribution @ %d procs\n", numProc);
      eputs(errmsg);
   }
               
   /* MAIN LOOP */
   for (proc = 0; proc < numProc; proc++) {
#if NIX          
      if (!(pid = fork())) {
         /* SUBPROCESS ENTERS HERE; opened and mmapped files inherited */
#else
      if (1) {
         /* No subprocess under Windoze */
#endif   
         /* Local photon counters for this subprocess */
         unsigned long  lastNumPhotons = 0, localNumEmitted = 0;
         double         photonFluxSum = 0;   /* Accum. photon flux */

         /* Seed RNGs from PID for decorellated photon distribution */
         pmapSeed(randSeed + proc, partState);
         pmapSeed(randSeed + (proc + 1) % numProc, emitState);
         pmapSeed(randSeed + (proc + 2) % numProc, cntState);
         pmapSeed(randSeed + (proc + 3) % numProc, mediumState);
         pmapSeed(randSeed + (proc + 4) % numProc, scatterState);
         pmapSeed(randSeed + (proc + 5) % numProc, rouletteState);

#ifdef PMAP_SIGUSR                       
   double partNumEmit;
   unsigned long partEmitCnt;
   double srcPhotonFlux, avgPhotonFlux;
   unsigned       portCnt, passCnt, prePassCnt;
   float          srcPreDistrib;
   double         srcNumEmit;     /* # to emit from source */
   unsigned long  srcNumDistrib;  /* # stored */

   void sigUsrDiags()
   /* Loop diags via SIGUSR1 */
   {
      sprintf(errmsg, 
              "********************* Proc %d Diags *********************\n"
              "srcIdx = %d (%s)\nportCnt = %d (%s)\npassCnt = %d\n"
              "srcFlux = %f\nsrcPhotonFlux = %f\navgPhotonFlux = %f\n"
              "partNumEmit = %f\npartEmitCnt = %lu\n\n",
              proc, srcIdx, findmaterial(source [srcIdx].so) -> oname, 
              portCnt, photonPorts [portCnt].so -> oname,
              passCnt, srcFlux [srcIdx], srcPhotonFlux, avgPhotonFlux,
              partNumEmit, partEmitCnt);
      eputs(errmsg);
      fflush(stderr);
   }
#endif
         
#ifdef PMAP_SIGUSR
         signal(SIGUSR1, sigUsrDiags);
#endif         

#ifdef DEBUG_PMAP          
         /* Output child process PID after random delay to prevent corrupted
          * console output due to race condition */
         usleep(1e6 * pmapRandom(rouletteState));
         fprintf(stderr, "Proc %d: PID = %d "
                 "(waiting 10 sec to attach debugger...)\n", 
                 proc, getpid());
         /* Allow time for debugger to attach to child process */
         sleep(10);
#endif

         /* =============================================================
          * 2-PASS PHOTON DISTRIBUTION
          * Pass 1 (pre):  emit fraction of target photon count
          * Pass 2 (main): based on outcome of pass 1, estimate remaining 
          *                number of photons to emit to approximate target 
          *                count
          * ============================================================= */
         for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
#ifndef PMAP_SIGUSR         
            unsigned       portCnt, passCnt = 0, prePassCnt = 0;
            float          srcPreDistrib = preDistrib;
            double         srcNumEmit = 0;       /* # to emit from source */
            unsigned long  srcNumDistrib = pm -> numPhotons;  /* # stored */
#else
            passCnt = prePassCnt = 0;
            srcPreDistrib = preDistrib;
            srcNumEmit = 0;       /* # to emit from source */
            srcNumDistrib = pm -> numPhotons;  /* # stored */
#endif            

            if (srcFlux [srcIdx] < FTINY)
               continue;
                        
            while (passCnt < 2) {
               if (!passCnt) {   
                  /* INIT PASS 1 */
                  if (++prePassCnt > maxPreDistrib) {
                     /* Warn if no photons contributed after sufficient
                      * iterations; only output from subprocess 0 to reduce
                      * console clutter */
                     if (!proc) {
                        sprintf(errmsg, 
                                "source %s: too many prepasses, skipped",
                                source [srcIdx].so -> oname);
                        error(WARNING, errmsg);
                     }

                     break;
                  }
                  
                  /* Num to emit is fraction of target count */
                  srcNumEmit = srcPreDistrib * srcDistribTarget;
               }
               else {
                  /* INIT PASS 2 */
#ifndef PMAP_SIGUSR
                  double srcPhotonFlux, avgPhotonFlux;
#endif
                  
                  /* Based on the outcome of the predistribution we can now
                   * figure out how many more photons we have to emit from
                   * the current source to meet the target count,
                   * srcDistribTarget. This value is clamped to 0 in case
                   * the target has already been exceeded in pass 1.
                   * srcNumEmit and srcNumDistrib is the number of photons
                   * emitted and distributed (stored) from the current
                   * source in pass 1, respectively. */
                  srcNumDistrib = pm -> numPhotons - srcNumDistrib;
                  srcNumEmit *= srcNumDistrib 
                                ? max(srcDistribTarget/srcNumDistrib, 1) - 1
                                : 0;

                  if (!srcNumEmit)
                     /* No photons left to distribute in main pass */
                     break;
                     
                  srcPhotonFlux = srcFlux [srcIdx] / srcNumEmit;
                  avgPhotonFlux = photonFluxSum / (srcIdx + 1);
                  
                  if (avgPhotonFlux > FTINY && 
                      srcPhotonFlux / avgPhotonFlux < FTINY) {
                     /* Skip source if its photon flux is grossly below the
                      * running average, indicating negligible contributions
                      * at the expense of excessive distribution time; only
                      * output from subproc 0 to reduce console clutter */
                     if (!proc) {
                        sprintf(errmsg, 
                                "source %s: itsy bitsy photon flux, skipped",
                                source [srcIdx].so -> oname);                     
                        error(WARNING, errmsg);
                     }

                     srcNumEmit = 0;   /* Or just break??? */
                  }
                        
                  /* Update sum of photon flux per light source */
                  photonFluxSum += srcPhotonFlux;
               }
                              
               portCnt = 0;
               do {    /* Need at least one iteration if no ports! */
                  emap.src = source + srcIdx;
                  emap.port = emap.src -> sflags & SDISTANT 
                              ? photonPorts + portCnt : NULL;
                  photonPartition [emap.src -> so -> otype] (&emap);

                  if (verbose && !proc) {
                     /* Output from subproc 0 only to avoid race condition
                      * on console I/O */
                     if (!passCnt)
                        sprintf(errmsg, "\tPREPASS %d on source %s ",
                                prePassCnt, source [srcIdx].so -> oname);
                     else 
                        sprintf(errmsg, "\tMAIN PASS on source %s ",
                                source [srcIdx].so -> oname);

                     if (emap.port) {
                        sprintf(errmsg2, "via port %s ", 
                                photonPorts [portCnt].so -> oname);
                        strcat(errmsg, errmsg2);
                     }

                     sprintf(errmsg2, "(%lu partitions)\n",
                             emap.numPartitions);
                     strcat(errmsg, errmsg2);                     
                     eputs(errmsg);
#if NIX                     
                     fflush(stderr);
#endif                     
                  }                
                  
                  for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions; 
                       emap.partitionCnt++) {
#ifndef PMAP_SIGUSR                       
                     double partNumEmit;
                     unsigned long partEmitCnt;
#endif
                     
                     /* Get photon origin within current source partishunn
                      * and build emission map */
                     photonOrigin [emap.src -> so -> otype] (&emap);
                     initPhotonEmission(&emap, pdfSamples);
                     
                     /* Number of photons to emit from ziss partishunn;
                      * scale according to its normalised contribushunn to
                      * the emitted source flux */                     
                     partNumEmit = srcNumEmit * colorAvg(emap.partFlux) /
                                   srcFlux [srcIdx];                    
                     partEmitCnt = (unsigned long)partNumEmit;
                                                               
                     /* Probabilistically account for fractional photons */
                     if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
                        partEmitCnt++;
                        
                     /* Update local and shared global emission counter */
                     photonCnt [PHOTONCNT_NUMEMIT(srcIdx)] += partEmitCnt;
                     localNumEmitted += partEmitCnt;                                    
                     
                     /* Integer counter avoids FP rounding errors during
                      * iteration */
                     while (partEmitCnt--) {
                        RAY photonRay;
                     
                        /* Emit photon according to PDF (if any), allocate
                         * associated primary ray, and trace through scene
                         * until absorbed/leaked; emitPhoton() sets the
                         * emitting light source index in photonRay */
                        emitPhoton(&emap, &photonRay);
#if 1
                        if (emap.port)
                           /* !!!  PHOTON PORT REJECTION SAMPLING HACK: set
                            * !!!  photon port as fake hit object for
                            * !!!  primary ray to check for intersection in
                            * !!!  tracePhoton() */                        
                           photonRay.ro = emap.port -> so;
#endif
                        newPhotonPrimary(pm, &photonRay, primaryHeap[proc]);
                        /* Set subprocess index in photonRay for post-
                         * distrib primary index linearisation; this is
                         * propagated with the primary index in photonRay
                         * and set for photon hits by newPhoton() */
                        PMAP_SETRAYPROC(&photonRay, proc);
                        tracePhoton(&photonRay);
                     }
                     
                     /* Update shared global photon count */                     
                     photonCnt [PHOTONCNT_NUMPHOT] += pm -> numPhotons - 
                                                      lastNumPhotons;
                     lastNumPhotons = pm -> numPhotons;
#if !NIX
                     /* Synchronous progress report on Windoze */
                     if (!proc && photonRepTime > 0 && 
                           time(NULL) >= repLastTime + photonRepTime) {
                        unsigned s;                        
                        repComplete = pm -> distribTarget * numProc;
                        repProgress = photonCnt [PHOTONCNT_NUMPHOT];
                        
                        for (repEmitted = 0, s = 0; s < nsources; s++)
                           repEmitted += photonCnt [PHOTONCNT_NUMEMIT(s)];

                        pmapDistribReport();
                     }
#endif
                  }

                  portCnt++;
               } while (portCnt < numPhotonPorts);                  

               if (pm -> numPhotons == srcNumDistrib) {
                  /* Double predistrib factor in case no photons were stored
                   * for this source and redo pass 1 */
                  srcPreDistrib *= 2;
               }
               else {
                  /* Now do pass 2 */
                  passCnt++;
               }
            }
         }
                        
         /* Flush heap buffa one final time to prevent data corruption */
         flushPhotonHeap(pm);         
         /* Flush final photon primary to primary heap file */
         newPhotonPrimary(pm, NULL, primaryHeap [proc]);
         /* Heap files closed automatically on exit
            fclose(pm -> heap);
            fclose(primaryHeap [proc]); */
                  
#ifdef DEBUG_PMAP
         sprintf(errmsg, "Proc %d total %ld photons\n", proc, 
                 pm -> numPhotons);
         eputs(errmsg);
         fflush(stderr);
#endif

#ifdef PMAP_SIGUSR
         signal(SIGUSR1, SIG_DFL);
#endif

#if NIX
         /* Terminate subprocess */
         exit(0);
#endif
      }
      else if (pid < 0)
         error(SYSTEM, "failed to fork subprocess in distribPhotonContrib");
   }

#if NIX
   /* PARENT PROCESS CONTINUES HERE */
#ifdef SIGCONT
   /* Enable progress report signal handler */
   signal(SIGCONT, pmapDistribReport);
#endif
   /* Wait for subprocesses to complete while reporting progress */
   proc = numProc;
   while (proc) {
      while (waitpid(-1, &stat, WNOHANG) > 0) {
         /* Subprocess exited; check status */
         if (!WIFEXITED(stat) || WEXITSTATUS(stat))
            error(USER, "failed photon distribution");
      
         --proc;
      }
      
      /* Nod off for a bit and update progress  */
      sleep(1);

      /* Asynchronous progress report from shared subprocess counters */      
      repComplete = pm -> distribTarget * numProc;
      repProgress = photonCnt [PHOTONCNT_NUMPHOT];      
      
      for (repEmitted = 0, srcIdx = 0; srcIdx < nsources; srcIdx++)
         repEmitted += photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];

      /* Get global photon count from shmem updated by subprocs */
      pm -> numPhotons = photonCnt [PHOTONCNT_NUMPHOT];

      if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
         pmapDistribReport();
#ifdef SIGCONT
      else signal(SIGCONT, pmapDistribReport);
#endif
   }
#endif /* NIX */

   /* ================================================================
    * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
    * ================================================================ */
#ifdef SIGCONT    
   /* Reset signal handler */
   signal(SIGCONT, SIG_DFL);
#endif   
   free(emap.samples);

   if (!pm -> numPhotons)
      error(USER, "empty contribution photon map");

   /* Load per-subprocess primary rays into pm -> primary array */
   /* Dumb compilers apparently need the char** cast */
   pm -> numPrimary = buildPrimaries(pm, primaryHeap, 
                                     (char**)primaryHeapFname,
                                     primaryOfs, numProc);
   if (!pm -> numPrimary)
      error(INTERNAL, "no primary rays in contribution photon map");
   
   /* Set photon flux per source */
   for (srcIdx = 0; srcIdx < nsources; srcIdx++)
      srcFlux [srcIdx] /= photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
#if NIX
   /* Photon counters no longer needed, unmap shared memory */
   munmap(photonCnt, sizeof(*photonCnt));
   close(shmFile);
   unlink(shmFname);
#else
   free(photonCnt);   
#endif      
   
   if (verbose) {
      eputs("\nBuilding contribution photon map...\n");
#if NIX      
      fflush(stderr);
#endif      
   }
   
   /* Build underlying data structure; heap is destroyed */
   buildPhotonMap(pm, srcFlux, primaryOfs, numProc);
   
   /* Free per-subprocess primary heap files */
   for (proc = 0; proc < numProc; proc++)
      free(primaryHeapFname [proc]);
      
   free(primaryHeapFname);
   free(primaryHeap);
   free(primaryOfs);
   
   if (verbose)
      eputs("\n");
}
