text:
/* This file contains code to parse ``mbox'' and ``mmdf'' style mailboxes */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "mutt.h"
#include "mailbox.h"
#include "mx.h"
#include "sort.h"
#include "copy.h"
#include "mutt_curses.h"

#ifdef USE_COMPRESSED
#include "compress.h"
#endif

#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <utime.h>
#include <sys/file.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

/* struct used by mutt_sync_mailbox() to store new offsets */
struct m_update_t
{
  short valid;
  LOFF_T hdr;
  LOFF_T body;
  long lines;
  LOFF_T length;
};

/* parameters:
 * ctx - context to lock
 * excl - exclusive lock?
 * retry - should retry if unable to lock?
 */
int mbox_lock_mailbox (CONTEXT *ctx, int excl, int retry)
{
  int r;

  if ((r = mx_lock_file (ctx->path, fileno (ctx->fp), excl, 1, retry)) == 0)
    ctx->locked = 1;
  else if (retry && !excl)
  {
    ctx->readonly = 1;
    return 0;
  }

  return (r);
}

void mbox_unlock_mailbox (CONTEXT *ctx)
{
  if (ctx->locked)
  {
    fflush (ctx->fp);

    mx_unlock_file (ctx->path, fileno (ctx->fp), 1);
    ctx->locked = 0;
  }
}

int mmdf_parse_mailbox (CONTEXT *ctx)
{
  char buf[HUGE_STRING];
  char return_path[LONG_STRING];
  int count = 0, oldmsgcount = ctx->msgcount;
  int lines;
  time_t t;
  LOFF_T loc, tmploc;
  HEADER *hdr;
  struct stat sb;
#ifdef NFS_ATTRIBUTE_HACK
  struct utimbuf newtime;
#endif
  progress_t progress;
  char msgbuf[STRING];

  if (stat (ctx->path, &sb) == -1)
  {
    mutt_perror (ctx->path);
    return (-1);
  }
  ctx->atime = sb.st_atime;
  ctx->mtime = sb.st_mtime;
  ctx->size = sb.st_size;

#ifdef NFS_ATTRIBUTE_HACK
  if (sb.st_mtime > sb.st_atime)
  {
    newtime.modtime = sb.st_mtime;
    newtime.actime = time (NULL);
    utime (ctx->path, &newtime);
  }
#endif

  buf[sizeof (buf) - 1] = 0;

  if (!ctx->quiet)
  {
    snprintf (msgbuf, sizeof (msgbuf), _("Reading %s..."), ctx->path);
    mutt_progress_init (&progress, msgbuf, M_PROGRESS_MSG, ReadInc, 0);
  }

  FOREVER
  {
    if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
      break;

    if (mutt_strcmp (buf, MMDF_SEP) == 0)
    {
      loc = ftello (ctx->fp);

      count++;
      if (!ctx->quiet)
	mutt_progress_update (&progress, count,
			      (int) (loc / (ctx->size / 100 + 1)));

      if (ctx->msgcount == ctx->hdrmax)
	mx_alloc_memory (ctx);
      ctx->hdrs[ctx->msgcount] = hdr = mutt_new_header ();
      hdr->offset = loc;
      hdr->index = ctx->msgcount;

      if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
      {
	/* TODO: memory leak??? */
	dprint (1, (debugfile, "mmdf_parse_mailbox: unexpected EOF\n"));
	break;
      }

      return_path[0] = 0;

      if (!is_from (buf, return_path, sizeof (return_path), &t))
      {
	if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
	{
	  dprint (1, (debugfile, "mmdf_parse_mailbox: fseek() failed\n"));
	  mutt_error _("Mailbox is corrupt!");
	  return (-1);
	}
      }
      else
	hdr->received = t - mutt_local_tz (t);

      hdr->env = mutt_read_rfc822_header (ctx->fp, hdr, 0, 0);

      loc = ftello (ctx->fp);

      if (hdr->content->length > 0 && hdr->lines > 0)
      {
	tmploc = loc + hdr->content->length;

	if (0 < tmploc && tmploc < ctx->size)
	{
	  if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
	      fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL ||
	      mutt_strcmp (MMDF_SEP, buf) != 0)
	  {
	    if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
	      dprint (1, (debugfile, "mmdf_parse_mailbox: fseek() failed\n"));
	    hdr->content->length = -1;
	  }
	}
	else
	  hdr->content->length = -1;
      }
      else
	hdr->content->length = -1;

      if (hdr->content->length < 0)
      {
	lines = -1;
	do {
	  loc = ftello (ctx->fp);
	  if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
	    break;
	  lines++;
	} while (mutt_strcmp (buf, MMDF_SEP) != 0);

	hdr->lines = lines;
	hdr->content->length = loc - hdr->content->offset;
      }

      if (!hdr->env->return_path && return_path[0])
	hdr->env->return_path = rfc822_parse_adrlist (hdr->env->return_path, return_path);

      if (!hdr->env->from)
	hdr->env->from = rfc822_cpy_adr (hdr->env->return_path, 0);

      ctx->msgcount++;
    }
    else
    {
      dprint (1, (debugfile, "mmdf_parse_mailbox: corrupt mailbox!\n"));
      mutt_error _("Mailbox is corrupt!");
      return (-1);
    }
  }

  if (ctx->msgcount > oldmsgcount)
    mx_update_context (ctx, ctx->msgcount - oldmsgcount);

  return (0);
}

/* Note that this function is also called when new mail is appended to the
 * currently open folder, and NOT just when the mailbox is initially read.
 *
 * NOTE: it is assumed that the mailbox being read has been locked before
 * this routine gets called.  Strange things could happen if it's not!
 */
int mbox_parse_mailbox (CONTEXT *ctx)
{
  struct stat sb;
  char buf[HUGE_STRING], return_path[STRING];
  HEADER *curhdr;
  time_t t;
  int count = 0, lines = 0;
  LOFF_T loc;
#ifdef NFS_ATTRIBUTE_HACK
  struct utimbuf newtime;
#endif
  progress_t progress;
  char msgbuf[STRING];

  /* Save information about the folder at the time we opened it. */
  if (stat (ctx->path, &sb) == -1)
  {
    mutt_perror (ctx->path);
    return (-1);
  }

  ctx->size = sb.st_size;
  ctx->mtime = sb.st_mtime;
  ctx->atime = sb.st_atime;

#ifdef NFS_ATTRIBUTE_HACK
  if (sb.st_mtime > sb.st_atime)
  {
    newtime.modtime = sb.st_mtime;
    newtime.actime = time (NULL);
    utime (ctx->path, &newtime);
  }
#endif

  if (!ctx->readonly)
    ctx->readonly = access (ctx->path, W_OK) ? 1 : 0;

  if (!ctx->quiet)
  {
    snprintf (msgbuf, sizeof (msgbuf), _("Reading %s..."), ctx->path);
    mutt_progress_init (&progress, msgbuf, M_PROGRESS_MSG, ReadInc, 0);
  }

  loc = ftello (ctx->fp);
  while (fgets (buf, sizeof (buf), ctx->fp) != NULL)
  {
    if (is_from (buf, return_path, sizeof (return_path), &t))
    {
      /* Save the Content-Length of the previous message */
      if (count > 0)
      {
#define PREV ctx->hdrs[ctx->msgcount-1]

	if (PREV->content->length < 0)
	{
	  PREV->content->length = loc - PREV->content->offset - 1;
	  if (PREV->content->length < 0)
	    PREV->content->length = 0;
	}
	if (!PREV->lines)
	  PREV->lines = lines ? lines - 1 : 0;
      }

      count++;

      if (!ctx->quiet)
	mutt_progress_update (&progress, count,
			      (int)(ftello (ctx->fp) / (ctx->size / 100 + 1)));

      if (ctx->msgcount == ctx->hdrmax)
	mx_alloc_memory (ctx);

      curhdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
      curhdr->received = t - mutt_local_tz (t);
      curhdr->offset = loc;
      curhdr->index = ctx->msgcount;

      curhdr->env = mutt_read_rfc822_header (ctx->fp, curhdr, 0, 0);

      /* if we know how long this message is, either just skip over the body,
       * or if we don't know how many lines there are, count them now (this will
       * save time by not having to search for the next message marker).
       */
      if (curhdr->content->length > 0)
      {
	LOFF_T tmploc;

	loc = ftello (ctx->fp);
	tmploc = loc + curhdr->content->length + 1;

	if (0 < tmploc && tmploc < ctx->size)
	{
	  /*
	   * check to see if the content-length looks valid.  we expect to
	   * to see a valid message separator at this point in the stream
	   */
	  if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0 ||
	      fgets (buf, sizeof (buf), ctx->fp) == NULL ||
	      mutt_strncmp ("From ", buf, 5) != 0)
	  {
	    dprint (1, (debugfile, "mbox_parse_mailbox: bad content-length in message %d (cl=" OFF_T_FMT ")\n", curhdr->index, curhdr->content->length));
	    dprint (1, (debugfile, "\tLINE: %s", buf));
	    if (fseeko (ctx->fp, loc, SEEK_SET) != 0) /* nope, return the previous position */
	    {
	      dprint (1, (debugfile, "mbox_parse_mailbox: fseek() failed\n"));
	    }
	    curhdr->content->length = -1;
	  }
	}
	else if (tmploc != ctx->size)
	{
	  /* content-length would put us past the end of the file, so it
	   * must be wrong
	   */
	  curhdr->content->length = -1;
	}

	if (curhdr->content->length != -1)
	{
	  /* good content-length.  check to see if we know how many lines
	   * are in this message.
	   */
	  if (curhdr->lines == 0)
	  {
	    int cl = curhdr->content->length;

	    /* count the number of lines in this message */
	    if (fseeko (ctx->fp, loc, SEEK_SET) != 0)
	      dprint (1, (debugfile, "mbox_parse_mailbox: fseek() failed\n"));
	    while (cl-- > 0)
	    {
	      if (fgetc (ctx->fp) == '\n')
		curhdr->lines++;
	    }
	  }

	  /* return to the offset of the next message separator */
	  if (fseeko (ctx->fp, tmploc, SEEK_SET) != 0)
	    dprint (1, (debugfile, "mbox_parse_mailbox: fseek() failed\n"));
	}
      }

      ctx->msgcount++;

      if (!curhdr->env->return_path && return_path[0])
	curhdr->env->return_path = rfc822_parse_adrlist (curhdr->env->return_path, return_path);

      if (!curhdr->env->from)
	curhdr->env->from = rfc822_cpy_adr (curhdr->env->return_path, 0);

      lines = 0;
    }
    else
      lines++;

    loc = ftello (ctx->fp);
  }

  /*
   * Only set the content-length of the previous message if we have read more
   * than one message during _this_ invocation.  If this routine is called
   * when new mail is received, we need to make sure not to clobber what
   * previously was the last message since the headers may be sorted.
   */
  if (count > 0)
  {
    if (PREV->content->length < 0)
    {
      PREV->content->length = ftello (ctx->fp) - PREV->content->offset - 1;
      if (PREV->content->length < 0)
	PREV->content->length = 0;
    }

    if (!PREV->lines)
      PREV->lines = lines ? lines - 1 : 0;

    mx_update_context (ctx, count);
  }

  return (0);
}

#undef PREV

/* open a mbox or mmdf style mailbox */
int mbox_open_mailbox (CONTEXT *ctx)
{
  int rc;

  if ((ctx->fp = fopen (ctx->path, "r")) == NULL)
  {
    mutt_perror (ctx->path);
    return (-1);
  }
  mutt_block_signals ();
  if (mbox_lock_mailbox (ctx, 0, 1) == -1)
  {
    mutt_unblock_signals ();
    return (-1);
  }

  if (ctx->magic == M_MBOX)
    rc = mbox_parse_mailbox (ctx);
  else if (ctx->magic == M_MMDF)
    rc = mmdf_parse_mailbox (ctx);
  else
    rc = -1;

  mbox_unlock_mailbox (ctx);
  mutt_unblock_signals ();
  return (rc);
}

/* return 1 if address lists are strictly identical */
static int strict_addrcmp (const ADDRESS *a, const ADDRESS *b)
{
  while (a && b)
  {
    if (mutt_strcmp (a->mailbox, b->mailbox) ||
	mutt_strcmp (a->personal, b->personal))
      return (0);

    a = a->next;
    b = b->next;
  }
  if (a || b)
    return (0);

  return (1);
}

static int strict_cmp_lists (const LIST *a, const LIST *b)
{
  while (a && b)
  {
    if (mutt_strcmp (a->data, b->data))
      return (0);

    a = a->next;
    b = b->next;
  }
  if (a || b)
    return (0);

  return (1);
}

static int strict_cmp_envelopes (const ENVELOPE *e1, const ENVELOPE *e2)
{
  if (e1 && e2)
  {
    if (mutt_strcmp (e1->message_id, e2->message_id) ||
	mutt_strcmp (e1->subject, e2->subject) ||
	!strict_cmp_lists (e1->references, e2->references) ||
	!strict_addrcmp (e1->from, e2->from) ||
	!strict_addrcmp (e1->sender, e2->sender) ||
	!strict_addrcmp (e1->reply_to, e2->reply_to) ||
	!strict_addrcmp (e1->to, e2->to) ||
	!strict_addrcmp (e1->cc, e2->cc) ||
	!strict_addrcmp (e1->return_path, e2->return_path))
      return (0);
    else
      return (1);
  }
  else
  {
    if (e1 == NULL && e2 == NULL)
      return (1);
    else
      return (0);
  }
}

static int strict_cmp_parameters (const PARAMETER *p1, const PARAMETER *p2)
{
  while (p1 && p2)
  {
    if (mutt_strcmp (p1->attribute, p2->attribute) ||
	mutt_strcmp (p1->value, p2->value))
      return (0);

    p1 = p1->next;
    p2 = p2->next;
  }
  if (p1 || p2)
    return (0);

  return (1);
}

static int strict_cmp_bodies (const BODY *b1, const BODY *b2)
{
  if (b1->type != b2->type ||
      b1->encoding != b2->encoding ||
      mutt_strcmp (b1->subtype, b2->subtype) ||
      mutt_strcmp (b1->description, b2->description) ||
      !strict_cmp_parameters (b1->parameter, b2->parameter) ||
      b1->length != b2->length)
    return (0);
  return (1);
}

/* return 1 if headers are strictly identical */
int mbox_strict_cmp_headers (const HEADER *h1, const HEADER *h2)
{
  if (h1 && h2)
  {
    if (h1->received != h2->received ||
	h1->date_sent != h2->date_sent ||
	h1->content->length != h2->content->length ||
	h1->lines != h2->lines ||
	h1->zhours != h2->zhours ||
	h1->zminutes != h2->zminutes ||
	h1->zoccident != h2->zoccident ||
	h1->mime != h2->mime ||
	!strict_cmp_envelopes (h1->env, h2->env) ||
	!strict_cmp_bodies (h1->content, h2->content))
      return (0);
    else
      return (1);
  }
  else
  {
    if (h1 == NULL && h2 == NULL)
      return (1);
    else
      return (0);
  }
}

/* check to see if the mailbox has changed on disk.
 *
 * return values:
 *	M_REOPENED	mailbox has been reopened
 *	M_NEW_MAIL	new mail has arrived!
 *	M_LOCKED	couldn't lock the file
 *	0		no change
 *	-1		error
 */
int mbox_check_mailbox (CONTEXT *ctx, int *index_hint)
{
  struct stat st;
  char buffer[LONG_STRING];
  int unlock = 0;
  int modified = 0;

  if (stat (ctx->path, &st) == 0)
  {
    if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
      return (0);

    if (st.st_size == ctx->size)
    {
      /* the file was touched, but it is still the same length, so just exit */
      ctx->mtime = st.st_mtime;
      return (0);
    }

    if (st.st_size > ctx->size)
    {
      /* lock the file if it isn't already */
      if (!ctx->locked)
      {
	mutt_block_signals ();
	if (mbox_lock_mailbox (ctx, 0, 0) == -1)
	{
	  mutt_unblock_signals ();
	  /* we couldn't lock the mailbox, but nothing serious happened:
	   * probably the new mail arrived: no reason to wait till we can
	   * parse it: we'll get it on the next pass
	   */
	  return (M_LOCKED);
	}
	unlock = 1;
      }

      /*
       * Check to make sure that the only change to the mailbox is that
       * message(s) were appended to this file.  My heuristic is that we should
       * see the message separator at *exactly* what used to be the end of the
       * folder.
       */
      if (fseeko (ctx->fp, ctx->size, SEEK_SET) != 0)
	dprint (1, (debugfile, "mbox_check_mailbox: fseek() failed\n"));
      if (fgets (buffer, sizeof (buffer), ctx->fp) != NULL)
      {
	if ((ctx->magic == M_MBOX && mutt_strncmp ("From ", buffer, 5) == 0) ||
	    (ctx->magic == M_MMDF && mutt_strcmp (MMDF_SEP, buffer) == 0))
	{
	  if (fseeko (ctx->fp, ctx->size, SEEK_SET) != 0)
	    dprint (1, (debugfile, "mbox_check_mailbox: fseek() failed\n"));
	  if (ctx->magic == M_MBOX)
	    mbox_parse_mailbox (ctx);
	  else
	    mmdf_parse_mailbox (ctx);

	  /* Only unlock the folder if it was locked inside of this routine.
	   * It may have been locked elsewhere, like in
	   * mutt_checkpoint_mailbox().
	   */

	  if (unlock)
	  {
	    mbox_unlock_mailbox (ctx);
	    mutt_unblock_signals ();
	  }

	  return (M_NEW_MAIL); /* signal that new mail arrived */
	}
	else
	  modified = 1;
      }
      else
      {
	dprint (1, (debugfile, "mbox_check_mailbox: fgets returned NULL.\n"));
	modified = 1;
      }
    }
    else
      modified = 1;
  }

  if (modified)
  {
    if (mutt_reopen_mailbox (ctx, index_hint) != -1)
    {
      if (unlock)
      {
	mbox_unlock_mailbox (ctx);
	mutt_unblock_signals ();
      }
      return (M_REOPENED);
    }
  }

  /* fatal error */

  mbox_unlock_mailbox (ctx);
  mx_fastclose_mailbox (ctx);
  mutt_unblock_signals ();
  mutt_error _("Mailbox was corrupted!");
  return (-1);
}

/*
 * Returns 1 if the mailbox has at least 1 new messages (not old)
 * otherwise returns 0.
 */
static int mbox_has_new(CONTEXT *ctx)
{
  int i;

  for (i = 0; i < ctx->msgcount; i++)
    if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->read && !ctx->hdrs[i]->old)
      return 1;
  return 0;
}

/* if mailbox has at least 1 new message, sets mtime > atime of mailbox
 * so buffy check reports new mail */
void mbox_reset_atime (CONTEXT *ctx, struct stat *st)
{
  struct utimbuf utimebuf;
  struct stat _st;

  if (!st)
  {
    if (stat (ctx->path, &_st) < 0)
      return;
    st = &_st;
  }

  utimebuf.actime = st->st_atime;
  utimebuf.modtime = st->st_mtime;

  /*
   * When $mbox_check_recent is set, existing new mail is ignored, so do not
   * reset the atime to mtime-1 to signal new mail.
   */
  if (!option(OPTMAILCHECKRECENT) && utimebuf.actime >= utimebuf.modtime && mbox_has_new(ctx))
    utimebuf.actime = utimebuf.modtime - 1;

  utime (ctx->path, &utimebuf);
}

/* return values:
 *	0	success
 *	-1	failure
 */
int mbox_sync_mailbox (CONTEXT *ctx, int *index_hint)
{
  char tempfile[_POSIX_PATH_MAX];
  char buf[32];
  int i, j, save_sort = SORT_ORDER;
  int rc = -1;
  int need_sort = 0; /* flag to resort mailbox if new mail arrives */
  int first = -1;	/* first message to be written */
  LOFF_T offset;	/* location in mailbox to write changed messages */
  struct stat statbuf;
  struct m_update_t *newOffset = NULL;
  struct m_update_t *oldOffset = NULL;
  FILE *fp = NULL;
  progress_t progress;
  char msgbuf[STRING];

  /* sort message by their position in the mailbox on disk */
  if (Sort != SORT_ORDER)
  {
    save_sort = Sort;
    Sort = SORT_ORDER;
    mutt_sort_headers (ctx, 0);
    Sort = save_sort;
    need_sort = 1;
  }

  /* need to open the file for writing in such a way that it does not truncate
   * the file, so use read-write mode.
   */
  if ((ctx->fp = freopen (ctx->path, "r+", ctx->fp)) == NULL)
  {
    mx_fastclose_mailbox (ctx);
    mutt_error _("Fatal error!  Could not reopen mailbox!");
    return (-1);
  }

  mutt_block_signals ();

  if (mbox_lock_mailbox (ctx, 1, 1) == -1)
  {
    mutt_unblock_signals ();
    mutt_error _("Unable to lock mailbox!");
    goto bail;
  }

  /* Check to make sure that the file hasn't changed on disk */
  if ((i = mbox_check_mailbox (ctx, index_hint)) == M_NEW_MAIL ||  i == M_REOPENED)
  {
    /* new mail arrived, or mailbox reopened */
    need_sort = i;
    rc = i;
    goto bail;
  }
  else if (i < 0)
    /* fatal error */
    return (-1);

  /* Create a temporary file to write the new version of the mailbox in. */
  mutt_mktemp (tempfile, sizeof (tempfile));
  if ((i = open (tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1 ||
      (fp = fdopen (i, "w")) == NULL)
  {
    if (-1 != i)
    {
      close (i);
      unlink (tempfile);
    }
    mutt_error _("Could not create temporary file!");
    mutt_sleep (5);
    goto bail;
  }

  /* find the first deleted/changed message.  we save a lot of time by only
   * rewriting the mailbox from the point where it has actually changed.
   */
  for (i = 0 ; i < ctx->msgcount && !ctx->hdrs[i]->deleted &&
               !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++)
    ;
  if (i == ctx->msgcount)
  {
    /* this means ctx->changed or ctx->deleted was set, but no
     * messages were found to be changed or deleted.  This should
     * never happen, is we presume it is a bug in mutt.
     */
    mutt_error _("sync: mbox modified, but no modified messages! (report this bug)");
    mutt_sleep(5); /* the mutt_error /will/ get cleared! */
    dprint(1, (debugfile, "mbox_sync_mailbox(): no modified messages.\n"));
    unlink (tempfile);
    goto bail;
  }

    /* save the index of the first changed/deleted message */
  first = i;
  /* where to start overwriting */
  offset = ctx->hdrs[i]->offset;

  /* the offset stored in the header does not include the MMDF_SEP, so make
   * sure we seek to the correct location
   */
  if (ctx->magic == M_MMDF)
    offset -= (sizeof MMDF_SEP - 1);

  /* allocate space for the new offsets */
  newOffset = safe_calloc (ctx->msgcount - first, sizeof (struct m_update_t));
  oldOffset = safe_calloc (ctx->msgcount - first, sizeof (struct m_update_t));

  if (!ctx->quiet)
  {
    snprintf (msgbuf, sizeof (msgbuf), _("Writing %s..."), ctx->path);
    mutt_progress_init (&progress, msgbuf, M_PROGRESS_MSG, WriteInc, ctx->msgcount);
  }

  for (i = first, j = 0; i < ctx->msgcount; i++)
  {
    if (!ctx->quiet)
      mutt_progress_update (&progress, i, (int)(ftello (ctx->fp) / (ctx->size / 100 + 1)));
    /*
     * back up some information which is needed to restore offsets when
     * something fails.
     */

    oldOffset[i-first].valid  = 1;
    oldOffset[i-first].hdr    = ctx->hdrs[i]->offset;
    oldOffset[i-first].body   = ctx->hdrs[i]->content->offset;
    oldOffset[i-first].lines  = ctx->hdrs[i]->lines;
    oldOffset[i-first].length = ctx->hdrs[i]->content->length;

    if (! ctx->hdrs[i]->deleted)
    {
      j++;

      if (ctx->magic == M_MMDF)
      {
	if (fputs (MMDF_SEP, fp) == EOF)
	{
	  mutt_perror (tempfile);
	  mutt_sleep (5);
	  unlink (tempfile);
	  goto bail;
	}

      }

      /* save the new offset for this message.  we add `offset' because the
       * temporary file only contains saved message which are located after
       * `offset' in the real mailbox
       */
      newOffset[i - first].hdr = ftello (fp) + offset;

      if (mutt_copy_message (fp, ctx, ctx->hdrs[i], M_CM_UPDATE,
                             CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0)
      {
	mutt_perror (tempfile);
	mutt_sleep (5);
	unlink (tempfile);
	goto bail;
      }

      /* Since messages could have been deleted, the offsets stored in memory
       * will be wrong, so update what we can, which is the offset of this
       * message, and the offset of the body.  If this is a multipart message,
       * we just flush the in memory cache so that the message will be reparsed
       * if the user accesses it later.
       */
      newOffset[i - first].body = ftello (fp) - ctx->hdrs[i]->content->length + offset;
      mutt_free_body (&ctx->hdrs[i]->content->parts);

      switch(ctx->magic)
      {
	case M_MMDF:
	  if(fputs(MMDF_SEP, fp) == EOF)
	  {
	    mutt_perror (tempfile);
	    mutt_sleep (5);
	    unlink (tempfile);
	    goto bail;
	  }
	  break;
	default:
	  if(fputs("\n", fp) == EOF)
	  {
	    mutt_perror (tempfile);
	    mutt_sleep (5);
	    unlink (tempfile);
	    goto bail;
	  }
      }
    }
  }

  if (fclose (fp) != 0)
  {
    fp = NULL;
    dprint(1, (debugfile, "mbox_sync_mailbox: safe_fclose (&) returned non-zero.\n"));
    unlink (tempfile);
    mutt_perror (tempfile);
    mutt_sleep (5);
    goto bail;
  }
  fp = NULL;

  /* Save the state of this folder. */
  if (stat (ctx->path, &statbuf) == -1)
  {
    mutt_perror (ctx->path);
    mutt_sleep (5);
    unlink (tempfile);
    goto bail;
  }

  if ((fp = fopen (tempfile, "r")) == NULL)
  {
    mutt_unblock_signals ();
    mx_fastclose_mailbox (ctx);
    dprint (1, (debugfile, "mbox_sync_mailbox: unable to reopen temp copy of mailbox!\n"));
    mutt_perror (tempfile);
    mutt_sleep (5);
    return (-1);
  }

  if (fseeko (ctx->fp, offset, SEEK_SET) != 0 ||  /* seek the append location */
      /* do a sanity check to make sure the mailbox looks ok */
      fgets (buf, sizeof (buf), ctx->fp) == NULL ||
      (ctx->magic == M_MBOX && mutt_strncmp ("From ", buf, 5) != 0) ||
      (ctx->magic == M_MMDF && mutt_strcmp (MMDF_SEP, buf) != 0))
  {
    dprint (1, (debugfile, "mbox_sync_mailbox: message not in expected position."));
    dprint (1, (debugfile, "\tLINE: %s\n", buf));
    i = -1;
  }
  else
  {
    if (fseeko (ctx->fp, offset, SEEK_SET) != 0) /* return to proper offset */
    {
      i = -1;
      dprint (1, (debugfile, "mbox_sync_mailbox: fseek() failed\n"));
    }
    else
    {
      /* copy the temp mailbox back into place starting at the first
       * change/deleted message
       */
      if (!ctx->quiet)
	mutt_message _("Committing changes...");
      i = mutt_copy_stream (fp, ctx->fp);

      if (ferror (ctx->fp))
        i = -1;
    }
    if (i == 0)
    {
      ctx->size = ftello (ctx->fp); /* update the size of the mailbox */
      ftruncate (fileno (ctx->fp), ctx->size);
    }
  }

  safe_fclose (&fp);
  fp = NULL;
  mbox_unlock_mailbox (ctx);

  if (fclose (ctx->fp) != 0 || i == -1)
  {
    /* error occurred while writing the mailbox back, so keep the temp copy
     * around
     */

    char savefile[_POSIX_PATH_MAX];

    snprintf (savefile, sizeof (savefile), "%s/mutt.%s-%s-%u",
	      NONULL (Tempdir), NONULL(Username), NONULL(Hostname), (unsigned int)getpid ());
    rename (tempfile, savefile);
    mutt_unblock_signals ();
    mx_fastclose_mailbox (ctx);
    mutt_pretty_mailbox (savefile, sizeof (savefile));
    mutt_error (_("Write failed!  Saved partial mailbox to %s"), savefile);
    mutt_sleep (5);
    return (-1);
  }

  /* Restore the previous access/modification times */
  mbox_reset_atime (ctx, &statbuf);

  /* reopen the mailbox in read-only mode */
  if ((ctx->fp = fopen (ctx->path, "r")) == NULL)
  {
    unlink (tempfile);
    mutt_unblock_signals ();
    mx_fastclose_mailbox (ctx);
    mutt_error _("Fatal error!  Could not reopen mailbox!");
    return (-1);
  }

  /* update the offsets of the rewritten messages */
  for (i = first, j = first; i < ctx->msgcount; i++)
  {
    if (!ctx->hdrs[i]->deleted)
    {
      ctx->hdrs[i]->offset = newOffset[i - first].hdr;
      ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr;
      ctx->hdrs[i]->content->offset = newOffset[i - first].body;
      ctx->hdrs[i]->index = j++;
    }
  }
  FREE (&newOffset);
  FREE (&oldOffset);
  unlink (tempfile); /* remove partial copy of the mailbox */
  mutt_unblock_signals ();

  return (0); /* signal success */

bail:  /* Come here in case of disaster */

  safe_fclose (&fp);

  /* restore offsets, as far as they are valid */
  if (first >= 0 && oldOffset)
  {
    for (i = first; i < ctx->msgcount && oldOffset[i-first].valid; i++)
    {
      ctx->hdrs[i]->offset = oldOffset[i-first].hdr;
      ctx->hdrs[i]->content->hdr_offset = oldOffset[i-first].hdr;
      ctx->hdrs[i]->content->offset = oldOffset[i-first].body;
      ctx->hdrs[i]->lines = oldOffset[i-first].lines;
      ctx->hdrs[i]->content->length = oldOffset[i-first].length;
    }
  }

  /* this is ok to call even if we haven't locked anything */
  mbox_unlock_mailbox (ctx);

  mutt_unblock_signals ();
  FREE (&newOffset);
  FREE (&oldOffset);

  if ((ctx->fp = freopen (ctx->path, "r", ctx->fp)) == NULL)
  {
    mutt_error _("Could not reopen mailbox!");
    mx_fastclose_mailbox (ctx);
    return (-1);
  }

  if (need_sort)
    /* if the mailbox was reopened, the thread tree will be invalid so make
     * sure to start threading from scratch.  */
    mutt_sort_headers (ctx, (need_sort == M_REOPENED));

  return rc;
}

/* close a mailbox opened in write-mode */
int mbox_close_mailbox (CONTEXT *ctx)
{
  mx_unlock_file (ctx->path, fileno (ctx->fp), 1);

#ifdef USE_COMPRESSED
  if (ctx->compressinfo)
    mutt_slow_close_compressed (ctx);
#endif

  mutt_unblock_signals ();
  mx_fastclose_mailbox (ctx);
  return 0;
}

int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
{
  int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
  HEADER **old_hdrs;
  int old_msgcount;
  int msg_mod = 0;
  int index_hint_set;
  int i, j;
  int rc = -1;

  /* silent operations */
  ctx->quiet = 1;

  if (!ctx->quiet)
    mutt_message _("Reopening mailbox...");

  /* our heuristics require the old mailbox to be unsorted */
  if (Sort != SORT_ORDER)
  {
    short old_sort;

    old_sort = Sort;
    Sort = SORT_ORDER;
    mutt_sort_headers (ctx, 1);
    Sort = old_sort;
  }

  old_hdrs = NULL;
  old_msgcount = 0;

  /* simulate a close */
  if (ctx->id_hash)
    hash_destroy (&ctx->id_hash, NULL);
  if (ctx->subj_hash)
    hash_destroy (&ctx->subj_hash, NULL);
  mutt_clear_threads (ctx);
  FREE (&ctx->v2r);
  if (ctx->readonly)
  {
    for (i = 0; i < ctx->msgcount; i++)
      mutt_free_header (&(ctx->hdrs[i])); /* nothing to do! */
    FREE (&ctx->hdrs);
  }
  else
  {
      /* save the old headers */
    old_msgcount = ctx->msgcount;
    old_hdrs = ctx->hdrs;
    ctx->hdrs = NULL;
  }

  ctx->hdrmax = 0;	/* force allocation of new headers */
  ctx->msgcount = 0;
  ctx->vcount = 0;
  ctx->tagged = 0;
  ctx->deleted = 0;
  ctx->new = 0;
  ctx->unread = 0;
  ctx->flagged = 0;
  ctx->changed = 0;
  ctx->id_hash = NULL;
  ctx->subj_hash = NULL;

  switch (ctx->magic)
  {
    case M_MBOX:
    case M_MMDF:
      cmp_headers = mbox_strict_cmp_headers;
      safe_fclose (&ctx->fp);
      if (!(ctx->fp = safe_fopen (ctx->path, "r")))
	rc = -1;
      else
	rc = ((ctx->magic == M_MBOX) ? mbox_parse_mailbox
	                               : mmdf_parse_mailbox) (ctx);
      break;

    default:
      rc = -1;
      break;
  }

  if (rc == -1)
  {
    /* free the old headers */
    for (j = 0; j < old_msgcount; j++)
      mutt_free_header (&(old_hdrs[j]));
    FREE (&old_hdrs);

    ctx->quiet = 0;
    return (-1);
  }

  /* now try to recover the old flags */

  index_hint_set = (index_hint == NULL);

  if (!ctx->readonly)
  {
    for (i = 0; i < ctx->msgcount; i++)
    {
      int found = 0;

      /* some messages have been deleted, and new  messages have been
       * appended at the end; the heuristic is that old messages have then
       * "advanced" towards the beginning of the folder, so we begin the
       * search at index "i"
       */
      for (j = i; j < old_msgcount; j++)
      {
	if (old_hdrs[j] == NULL)
	  continue;
	if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
	{
	  found = 1;
	  break;
	}
      }
      if (!found)
      {
	for (j = 0; j < i && j < old_msgcount; j++)
	{
	  if (old_hdrs[j] == NULL)
	    continue;
	  if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
	  {
	    found = 1;
	    break;
	  }
	}
      }

      if (found)
      {
	/* this is best done here */
	if (!index_hint_set && *index_hint == j)
	  *index_hint = i;

	if (old_hdrs[j]->changed)
	{
	  /* Only update the flags if the old header was changed;
	   * otherwise, the header may have been modified externally,
	   * and we don't want to lose _those_ changes
	   */
	  mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, old_hdrs[j]->flagged);
	  mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, old_hdrs[j]->replied);
	  mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, old_hdrs[j]->old);
	  mutt_set_flag (ctx, ctx->hdrs[i], M_READ, old_hdrs[j]->read);
	}
	mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, old_hdrs[j]->deleted);
	mutt_set_flag (ctx, ctx->hdrs[i], M_TAG, old_hdrs[j]->tagged);

	/* we don't need this header any more */
	mutt_free_header (&(old_hdrs[j]));
      }
    }

    /* free the remaining old headers */
    for (j = 0; j < old_msgcount; j++)
    {
      if (old_hdrs[j])
      {
	mutt_free_header (&(old_hdrs[j]));
	msg_mod = 1;
      }
    }
    FREE (&old_hdrs);
  }

  ctx->quiet = 0;

  return ((ctx->changed || msg_mod) ? M_REOPENED : M_NEW_MAIL);
}

/*
 * Returns:
 * 1 if the mailbox is not empty
 * 0 if the mailbox is empty
 * -1 on error
 */
int mbox_check_empty (const char *path)
{
  struct stat st;

  if (stat (path, &st) == -1)
    return -1;

  return ((st.st_size == 0));
}


text:
namespace PaintsNow {
	class MultiHashSetupFS : public TReflected<MultiHashSetupFS, IShader> {
	public:
		MultiHashSetupFS();
		TObject<IReflect>& operator () (IReflect& reflect) override;
		String GetShaderText() override;

		BindBuffer setupParamBuffer;
		BindTexture noiseTexture;

		BindTexture lightDepthTexture;
		MatrixFloat4x4 lightReprojectionMatrix;
		Float4 lightColor;
		Float4 lightPosition;
		Float2 invScreenSize;

		Float4 rasterCoord;
		Float4 tintColor;

		// output
		Float4 outputIrradiance;
	};
}

redpajama_set_name: RedPajamaGithub


text:
//
//   Copyright 2018  SenX S.A.S.
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

package io.warp10.continuum.gts;

import io.warp10.script.GTSStackFunction;
import io.warp10.script.NamedWarpScriptFunction;
import io.warp10.script.WarpScriptStackFunction;
import io.warp10.script.WarpScriptException;
import io.warp10.script.WarpScriptStack;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Arrays;

/**
 * Detects discords in a GTS instance.
 *
 * The way discords are detected is by computing a histogram of bSAX symbols.
 * Those symbols with the lowest frequency are suspects for being discords, each one
 * is compared to its closest neighbours (we heuristically assume that bSAX symbols appearing
 * close in lexicographic order are the ones containing the nearest neighbours).
 * The 'count' suspects with highest distance to nearest neighbors are considered the discords.
 *
 * The expected parameters on the stack are:
 *
 * 6: GTS
 * 5: long windowLen
 * 4: long wordLen (must divide windowLen)
 * 3: long alphabetSize (must be a power of 2)
 * 2: long count, number of discords to identify
 * 1: boolean mayOverride
 */
public class DISCORDS extends GTSStackFunction {

  private static final String WINDOWLEN = "windowlen";
  private static final String WORDLEN = "wordlen";
  private static final String ALPHABETSIZE = "alphabetsize";
  private static final String COUNT = "count";
  private static final String OVERLAP = "overlap";
  private static final String DISTRATIO = "distratio";

  private final boolean standardizePAA;

  public DISCORDS(String name, boolean standardizePAA) {
    super(name);
    this.standardizePAA = standardizePAA;
  }

  @Override
  protected Object gtsOp(Map<String,Object> params, GeoTimeSerie gts) throws WarpScriptException {
    return discords(gts,
                    (int) params.get(WINDOWLEN),
                    (int) params.get(WORDLEN),
                    (int) params.get(ALPHABETSIZE),
                    (int) params.get(COUNT),
                    (boolean) params.get(OVERLAP),
                    (double) params.get(DISTRATIO),
                    this.standardizePAA);
  }

  @Override
  protected Map<String, Object> retrieveParameters(WarpScriptStack stack) throws WarpScriptException {

    Map<String,Object> params = new HashMap<String, Object>();

    Object dratio = stack.pop();

    if (!(dratio instanceof Double)) {
      throw new WarpScriptException(getName() + " expects a double distance ratio.");
    }

    double dr = (double) dratio;

    if (dr < 0.0D) {
      throw new WarpScriptException(getName() + " expects a positive distance ratio.");
    }

    params.put(DISTRATIO, (double) dr);

    Object overlap = stack.pop();

    if (!(overlap instanceof Boolean)) {
      throw new WarpScriptException(getName() + " expects a boolean flag to indicate tolerance to overlapping.");
    }
    params.put(OVERLAP, (boolean) overlap);

    Object count = stack.pop();

    if (!(count instanceof Long)) {
      throw new WarpScriptException(getName() + " expects an integer discord count.");
    }
    params.put(COUNT, ((Number) count).intValue());

    Object alphabetsize = stack.pop();

    if (!(alphabetsize instanceof Long)) {
      throw new WarpScriptException(getName() + " expects an integer quantization scale.");
    }
    int asize = ((Number) alphabetsize).intValue();

    if (0 != (asize & (asize - 1)) || asize < 2) {
      throw new WarpScriptException(getName() + " expects a quantization scale which is a power of 2 greater or equal to 2.");
    }
    params.put(ALPHABETSIZE, asize);

    Object wordlen = stack.pop();
    Object windowlen = stack.pop();

    if (!(wordlen instanceof Long)) {
      throw new WarpScriptException(getName() + " expects an integer pattern length.");
    }
    if (!(windowlen instanceof Long)) {
      throw new WarpScriptException(getName() + " expects an integer detection window length.");
    }

    int wolen = ((Number) wordlen).intValue();
    int wilen = ((Number) windowlen).intValue();

    if (0 != (wilen % wolen)) {
      throw new WarpScriptException(getName() + " expects pattern length to divide detection window length.");
    }
    params.put(WORDLEN, wolen);
    params.put(WINDOWLEN, wilen);

    return params;
  }

  /**
   *
   * @param gts GTS instance to analyze
   * @param windowLen Number of ticks in sliding window
   * @param wordLen Size of SAX symbols
   * @param alphabetSize Size of SAX alphabet (MUST be a power of 2)
   * @param count Maximum number of discords to identify
   * @param mayOverlap Flag indicating whether or not identified discords may overlap
   * @param distRatio Maximum tolerable ratio between NN distances. Once an NN dist is smaller than 'distRatio' times the previous candidate, exit
   * @return
   * @throws WarpScriptException
   */
  public static final GeoTimeSerie discords(GeoTimeSerie gts, int windowLen, int wordLen, int alphabetSize, int count, boolean mayOverlap, double distRatio, boolean standardizePAA) throws WarpScriptException {
    //
    // Compute bSAX symbols
    //
    GeoTimeSerie symbols = GTSHelper.bSAX(gts, alphabetSize, wordLen, windowLen, standardizePAA);

    //
    // Compute Map of symbol to list of locations where it appears
    //

    final SortedMap<String, List<Integer>> locationLists = new TreeMap<String, List<Integer>>();

    for (int i = 0; i < symbols.values; i++) {
      String symbol = GTSHelper.valueAtIndex(symbols, i).toString();
      if (!locationLists.containsKey(symbol)) {
        locationLists.put(symbol, new ArrayList<Integer>());
      }
      locationLists.get(symbol).add(i);
    }

    //
    // Sort symbols according to number of occurrences
    //

    List<String> symbolsByOccurrences = new ArrayList<String>();

    symbolsByOccurrences.addAll(locationLists.keySet());

    String[] rawSymbols = locationLists.keySet().toArray(new String[0]);

    Collections.sort(symbolsByOccurrences, new Comparator<String>() {
      @Override
      public int compare(String o1, String o2) {
        int n1 = locationLists.get(o1).size();
        int n2 = locationLists.get(o2).size();

        return n1 - n2;
      }
    });

    //
    // Convert List<Integer> to int[]
    //

    SortedMap<String,int[]> locations = new TreeMap<String,int[]>();

    for (String symbol: locationLists.keySet()) {
      List<Integer> ilocs = locationLists.get(symbol);
      int[] locs = new int[ilocs.size()];
      Collections.sort(ilocs);
      for (int i = 0; i < locs.length; i++) {
        locs[i] = ilocs.get(i);
      }
      locations.put(symbol, locs);
    }

    //
    // PriorityQueue for storing possible discords
    //

    final class DiscordCandidate {
      final String symbol;
      final int location;
      /**
       * Distance to nearest neighbour
       */
      final double nndist;

      public DiscordCandidate(String symbol, int location, double nndist) {
        this.symbol = symbol;
        this.location = location;
        this.nndist = nndist;
      }

      @Override
      public String toString() {
        return new StringBuilder().append(this.symbol).append("@").append(this.location).append(":").append(this.nndist).toString();
      }
    };

    List<DiscordCandidate> discords = new ArrayList<DiscordCandidate>();

    //
    // Worst NN distance so far. Any distance less than that means the candidate is not a discord
    //

    double worstNNDist = 0.0D;

    //
    // Loop over the symbols, from least common to most common
    //

    for (String symbol: symbolsByOccurrences) {

      int[] symbolLocations = locations.get(symbol);

      //
      // Exit the loop when we've already identified 'count' candidates and when the number
      // of occurrences of the current symbol is greater than 'windowLen' + 'count', meaning that
      // we will probably have a very close neighbor anyway
      //

      if (discords.size() >= count && symbolLocations.length > count + windowLen) {
        break;
      }

      //
      // Loop over each occurrence of the symbol
      //

      int size = symbolLocations.length;

      for (int i = 0; i < size; i++) {

        // Initialize NN dist to +infinity
        double nndist = Double.POSITIVE_INFINITY;

        //
        // Compute distance to nearest neighbor for the same symbol outside the window
        //

        for (int j = 0; j < size; j++) {
          if (Math.abs(symbolLocations[i] - symbolLocations[j]) < windowLen) {
            continue;
          }

          //
          // Compute distance to next location of the same symbol
          //

          double dist = 0.0D;

          for (int k = 0; k < windowLen; k++) {
            dist += Math.pow(((Number) GTSHelper.valueAtIndex(gts, symbolLocations[i] + k)).doubleValue()
                - ((Number) GTSHelper.valueAtIndex(gts, symbolLocations[j] + k)).doubleValue() , 2.0D);
          }

          if (dist < nndist) {
            nndist = dist;
          }

          //
          // If nndist is less than the worst NN Dist we've encountered and we already have 'count' candidates, exit the loop
          //

          if (discords.size() == count && nndist < worstNNDist) {
            break;
          }
        }

        //
        // If nndist is less than the worst NN Dist we've encountered and we already have 'count' candidates, exit the loop
        //

        if (discords.size() == count && nndist < worstNNDist) {
          continue;
        }

        //
        // Compute distance to neighbors with the symbol just before the current symbol
        //
        int idx = Arrays.binarySearch(rawSymbols, symbol);

        int idx2 = idx - 1;

        while(idx2 >= 0) {
          int[] symbolLocations2 = locations.get(rawSymbols[idx2]);
          for (int j = 0; j < symbolLocations2.length; j++) {
            if (Math.abs(symbolLocations[i] - symbolLocations2[j]) < windowLen) {
              continue;
            }

            //
            // Compute distance to next location of the same symbol
            //

            double dist = 0.0D;

            for (int k = 0; k < windowLen; k++) {
              dist += Math.pow(((Number) GTSHelper.valueAtIndex(gts, symbolLocations[i] + k)).doubleValue()
                  - ((Number) GTSHelper.valueAtIndex(gts, symbolLocations2[j] + k)).doubleValue() , 2.0D);
            }

            if (dist < nndist) {
              nndist = dist;
            }

            //
            // If nndist is less than the worst NN Dist we've encountered and we already have 'count' candidates, exit the loop
            //

            if (discords.size() == count && nndist < worstNNDist) {
              break;
            }
          }

          //
          // Exit if nndist is no longer +Infinity
          //

          if (Double.POSITIVE_INFINITY != nndist) {
            break;
          }

          idx2--;
        }

        if (discords.size() == count && nndist < worstNNDist) {
          continue;
        }

        //
        // Compute distance to neighbors with the symbol just after the current symbol
        //

        idx2 = rawSymbols.length + 1;

        while(idx2 < rawSymbols.length) {

          int[] symbolLocations2 = locations.get(rawSymbols[idx2]);
          for (int j = 0; j < symbolLocations2.length; j++) {
            if (Math.abs(symbolLocations[i] - symbolLocations2[j]) < windowLen) {
              continue;
            }

            //
            // Compute distance to next location of the same symbol
            //

            double dist = 0.0D;

            for (int k = 0; k < windowLen; k++) {
              dist += Math.pow(((Number) GTSHelper.valueAtIndex(gts, symbolLocations[i] + k)).doubleValue()
                  - ((Number) GTSHelper.valueAtIndex(gts, symbolLocations2[j] + k)).doubleValue() , 2.0D);
            }

            if (dist < nndist) {
              nndist = dist;
            }

            //
            // If nndist is less than the worst NN Dist we've encountered and we already have 'count' candidates, exit the loop
            //

            if (discords.size() == count && nndist < worstNNDist) {
              break;
            }
          }
          //
          // Exit if nndist is no longer +Infinity
          //

          if (Double.POSITIVE_INFINITY != nndist) {
            break;
          }

          idx2++;
        }

        if (discords.size() == count && nndist < worstNNDist) {
          continue;
        }

        //
        // Store nndist
        //

        DiscordCandidate candidate = new DiscordCandidate(symbol, symbolLocations[i], nndist);

        discords.add(candidate);

        //
        // Sort candidates according to decreasing nndist
        //

        Collections.sort(discords, new Comparator<DiscordCandidate>() {
          public int compare(DiscordCandidate o1, DiscordCandidate o2) {
            return (int) Math.signum(o2.nndist - o1.nndist);
          };
        });

        //
        // Remove overlapping candidates if they are forbidden
        //

        if (!mayOverlap) {
          for (int h = discords.size() - 1; h > 0; h--) {
            if (Math.abs(discords.get(h).location - discords.get(h - 1).location) < windowLen) {
              discords.remove(h);
              h--;
            }
          }
        }

        //
        // Only retain 'count' candidates
        //

        while(discords.size() > count) {
          discords.remove(count);
        }

        //
        // If 'distRatio' is not 0.0, remove any candidate whose nndist is smaller than '1/distRatio' the first candidate's nndist
        //

        if (0.0D != distRatio) {
          while(discords.size() > 1) {
            if (discords.get(discords.size() - 1).nndist * distRatio < discords.get(0).nndist) {
              discords.remove(discords.size() - 1);
            }
          }
        }

        //
        // Compute worstNNDist
        //

        worstNNDist = discords.get(discords.size() - 1).nndist;

        //
        // TODO(hbs): We would need to have an early exit once we think we won't find anymore candidates
        //
      }
    }

    //System.out.println(discords);

    /*
    for (String symbol: symbolsByOccurrences) {
      if (locations.get(symbol).size() > 100) {
        continue;
      }
      System.out.println(symbol + " >>> " + locations.get(symbol).size() + " >>> " + locations.get(symbol));
    }
    */

    //
    // Build the resulting GTS
    //

    GeoTimeSerie discordsGTS = gts.cloneEmpty();

    for (DiscordCandidate discord: discords) {
      for (int i = 0; i < windowLen; i++) {
        GTSHelper.setValue(discordsGTS,
            GTSHelper.tickAtIndex(gts, discord.location + i),
            GTSHelper.locationAtIndex(gts, discord.location + i),
            GTSHelper.elevationAtIndex(gts, discord.location + i),
            GTSHelper.valueAtIndex(gts, discord.location + i),
            false);
      }
    }

    return GTSHelper.dedup(discordsGTS);
  }
}


text:
"""A class for managing bases in decoupled Gaussian process model.

It manages the bases in the decoupled Gaussian process model so that the number
of bases can be added incrementally in order to ease optimization and add more
flexibility to the model. It is compatible of the usage of optimizers with
memory, such as adam and momentum.
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import logging
from decoupled_gaussian_process import utils
import numpy as np
import tensorflow as tf

class BasesMananger(object):
  """Manages the optimization procedure for bases."""

  def __init__(self,
               dim_bases,
               num_mean_bases,
               num_cov_bases,
               num_mean_bases_init,
               num_cov_bases_init,
               bases_to_sample=None,
               with_normalization=True,
               normalization_threshold=2048):
    """Initialize.

    Args:
      dim_bases: the dimension of bases.
      num_mean_bases: number of bases for mean.
      num_cov_bases: number of bases for covariance.
      num_mean_bases_init: initial number of bases for mean.
      num_cov_bases_init: initial number of bases for covariance.
      bases_to_sample: rank-2 tensor, bases to sample from to initialize
        bases for mean and covariance.
      with_normalization: bool, indicates whether statistics for inducing points
        will be normalized.
      normalization_threshold: integer, a parameter in normalization, in order
        to make training step less sensitive to number of bases.
    """

    # Make sure the initial size is no larger than final size.
    num_mean_bases_init = min(num_mean_bases, num_mean_bases_init)
    num_cov_bases_init = min(num_cov_bases, num_cov_bases_init)

    # Needed by _build_stats_normalizer()
    self.with_normalization = with_normalization
    self.normalization_threshold = normalization_threshold

    # Bases to initialize all the bases.
    # Make sure the bases for mean and covariance are initialized the same.
    max_num_bases = max(num_mean_bases, num_cov_bases)
    if bases_to_sample is not None:
      bases_init = self._build_bases_sampling(bases_to_sample, max_num_bases)
    else:
      stddev = (
          utils.EPSILON / np.sqrt(max_num_bases * dim_bases).astype(
              utils.np_float))
      bases_init = tf.truncated_normal(
          shape=[max_num_bases, dim_bases], stddev=stddev, dtype=utils.tf_float)

    # Couples mean and covariance bases when their initial numbers are the
    # same.
    is_coupled = num_mean_bases_init == num_cov_bases_init
    self._is_coupled = tf.get_variable(
        'is_coupled', trainable=False, initializer=is_coupled)

    # Mean bases, tf variables.
    with tf.variable_scope('mean'):
      (self._num_mean_bases, self._mean_bases, self._mean_stats,
       self._mean_log_coeff) = self._create_tf_variables(
           num_mean_bases, num_mean_bases_init, bases_init, [num_mean_bases, 1],
           dim_bases)

    # Covariance bases, tf variables.
    with tf.variable_scope('covariance'):
      (self._num_cov_bases, self._cov_bases,
       self._cov_stats, self._cov_log_coeff) = self._create_tf_variables(
           num_cov_bases, num_cov_bases_init, bases_init,
           [num_cov_bases, num_cov_bases], dim_bases)

      # Build non-tf or interfacing variables.
      self._build_public_variables()

  def _create_tf_variables(self, num_bases_max, num_bases_init, bases_init,
                           stats_shape, dim_bases):
    """Create tf variables for mean or covariance operators.

    Args:
      num_bases_max: the upper limit of the number of bases .
      num_bases_init: the initial number of bases.
      bases_init: rank-2 tensor or array, to initialize all the bases.
      stats_shape: list or array, the shape of the stats for the bases.
      dim_bases: dimension of bases.
    Returns:
      tuple of tf variables.
    """
    num_bases = tf.get_variable(
        'num_bases',
        initializer=num_bases_init,
        trainable=False,
        dtype=utils.tf_int)
    bases = tf.get_variable(
        'bases', initializer=bases_init[:num_bases_max], dtype=utils.tf_float)
    stddev = (
        utils.EPSILON / np.sqrt(np.prod(stats_shape)).astype(utils.np_float))
    stats = tf.get_variable(
        'stats',
        initializer=tf.truncated_normal(
            shape=stats_shape, stddev=stddev, dtype=utils.tf_float),
        dtype=utils.tf_float)
    log_coeff = tf.get_variable(
        'log_coeff',
        initializer=tf.truncated_normal(
            shape=[num_bases_max, dim_bases],
            stddev=utils.EPSILON,
            dtype=utils.tf_float),
        dtype=utils.tf_float)
    return num_bases, bases, stats, log_coeff

  def _build_public_variables(self):
    """Build public or idm_controlnterfacing variables."""
    # Mean bases.
    self.mean_bases = self._mean_bases[:self._num_mean_bases]
    self.mean_stats = self._mean_stats[:self._num_mean_bases]
    self.mean_coeff = tf.exp(self._mean_log_coeff[:self._num_mean_bases])

    # Covariance bases.
    self.cov_bases = tf.cond(self._is_coupled, lambda: self.mean_bases,
                             lambda: self._cov_bases[:self._num_cov_bases])
    self.cov_stats = self._cov_stats[:self._num_cov_bases, :self._num_cov_bases]
    self.cov_coeff = tf.cond(
        self._is_coupled, lambda: self.mean_coeff,
        lambda: tf.exp(self._cov_log_coeff[:self._num_cov_bases]))

    # Normalization based on the current number of bases.
    self.mean_stats /= self._build_stats_normalizer(self._num_mean_bases,
                                                    self.mean_stats)
    self.cov_stats /= self._build_stats_normalizer(self._num_cov_bases,
                                                   self.cov_stats)

  def _build_stats_normalizer(self, num_bases, stats):
    """Build normalizer for stats variable."""
    normalizer = tf.constant(1.0, utils.tf_float)
    if self.with_normalization:
      normalizer = tf.maximum(normalizer,
                              tf.cast(num_bases - self.normalization_threshold,
                                      utils.tf_float))
      # Adjust based on the number of elements in stats, so that the change in
      # the norm of stats for mean and covarance are the same.
      # exponent is 0.5 for mean, and 1.0 for covariance.
      exponent = tf.log(
          tf.cast(tf.reduce_prod(tf.shape(stats)), utils.tf_float))
      exponent /= 2.0 * tf.log(tf.cast(tf.shape(stats)[0], utils.tf_float))
      normalizer = tf.pow(normalizer, exponent)
    return normalizer

  def decouple_bases(self, session):
    """Decouples the bases for mean and covariance.

    Keeps the number of bases the same. After decouping, bases for mean and
    cov will be optimized separately.
    Args:
      session: tf session.
    Raises:
      ValueError: raised when the bases are already decoupled.
    """
    if session.run(self._is_coupled):
      # Copy part of mean bases to cov bases.
      session.run(
          tf.scatter_update(self._cov_bases, tf.range(self._num_cov_bases),
                            session.run(
                                self._mean_bases[:self._num_cov_bases])))
      session.run(
          tf.scatter_update(self._cov_log_coeff, tf.range(self._num_cov_bases),
                            session.run(
                                self._mean_log_coeff[:self._num_cov_bases])))
      session.run(tf.assign(self._is_coupled, False))
      logging.info('Bases are now decoupled.')
    else:
      raise ValueError('Bases have already been decoupled.')

  def add_bases(self, session, num_bases_to_add, bases_to_sample=None):
    """Adds more bases.

    Bases are incrementally added. Before reaching the max number, the same
    bases are added to both mean and covariance. But they can be optimized
    separately based on the coupling condition.

    Args:
      session: tf session.
      num_bases_to_add: int, number of bases to add to mean and covariance.
      bases_to_sample: rank-2 tensor, to sample from for initialization.
    """

    num_mean_bases_old = session.run(self._num_mean_bases)
    num_cov_bases_old = session.run(self._num_cov_bases)
    is_coupled = session.run(self._is_coupled)
    num_mean_bases_new = min(num_mean_bases_old + num_bases_to_add,
                             self._mean_bases.shape.as_list()[0])
    num_cov_bases_new = min(num_cov_bases_old + num_bases_to_add,
                            self._cov_bases.shape.as_list()[0])

    # Decouple bases if mean and covariance will have different number of bases,
    # due to their different max number.
    if (num_mean_bases_new != num_cov_bases_new) and is_coupled:
      self.decouple_bases(session)
      is_coupled = session.run(self._is_coupled)

    # Sample bases to initialize the newly added bases.
    bases_init = None
    if bases_to_sample is not None:
      bases_init = self._build_bases_sampling(bases_to_sample, num_bases_to_add)

    if num_mean_bases_new > num_mean_bases_old:
      logging.info('Updating num_mean_bases to %d.', num_mean_bases_new)
      self._add_bases_worker(session, num_mean_bases_new, num_mean_bases_old,
                             self._num_mean_bases, self._mean_bases,
                             self._mean_stats, bases_init)
    # Update the number of bases for covariance even it's coupled
    # with the mean, mainly due to stats which is not shared.
    if is_coupled:
      session.run(
          tf.assign(self._num_cov_bases, session.run(self._num_mean_bases)))
    if num_cov_bases_new > num_cov_bases_old and (not is_coupled):
      logging.info('Updating num_cov_bases to %d.', num_cov_bases_new)
      self._add_bases_worker(session, num_cov_bases_new, num_cov_bases_old,
                             self._num_cov_bases, self._cov_bases,
                             self._cov_stats, bases_init)

  def _add_bases_worker(self, session, num_bases_new, num_bases_old, num_bases,
                        bases, stats, bases_init):
    """Adds bases for mean or covariance.

    Args:
      session: tf session.
      num_bases_new: int, new number of bases.
      num_bases_old: int, old number of bases.
      num_bases: rank-0 tensor, number of bases.
      bases: rank-2 tensor, private.
      stats: rank-2 tensor, private.
      bases_init: rank-2 tensor or array, to initialize the new bases.
    """
    # Compensate stats for normalization.
    # New value of normalizer is read because it depends on num_bases, which
    # is just updated. So as the public stats. In order to keep the public
    # stats the same, we compensate for the change in normalizer by scaling
    # private stats.

    # Compute new value.
    stats_normalizer_old = session.run(
        self._build_stats_normalizer(num_bases_old, stats))
    stats_normalizer_new = session.run(
        self._build_stats_normalizer(num_bases_new, stats))
    stats_new = session.run(stats) * stats_normalizer_new / stats_normalizer_old

    # Assign.
    session.run(tf.assign(stats, stats_new))
    session.run(tf.assign(num_bases, num_bases_new))

    # Initialize newly added bases.
    if bases_init is not None:
      session.run(
          tf.scatter_update(bases, tf.range(num_bases_old, num_bases_new),
                            bases_init[:num_bases_new - num_bases_old]))

  # In order to satisfy tf.cond arguments types, and make code succinct.
  # pylint: disable=unnecessary-lambda
  # pylint: disable=g-long-lambda
  def build_conditional_bases_adding(self,
                                     global_step,
                                     bases_adding_freq,
                                     num_bases_to_add,
                                     bases_to_sample=None):
    """Build bases adding in tf graph that happens at certain global steps.

    Update public variables that subsume the logic of adding bases at certain
    global steps.

    Args:
      global_step: rank-0 tensor, global step in optimization.
      bases_adding_freq: int, adding bases per bases_adding_freq optimization.
      steps.
      num_bases_to_add: int, number of bases to add before reaching the limit.
      bases_to_sample: rank-2 tensor or array.
    """
    bases_adding = tf.cond(
        tf.equal(tf.mod(global_step, bases_adding_freq), 0),
        lambda: self._build_bases_adding(num_bases_to_add, bases_to_sample),
        lambda: tf.group())
    # Public variables which are non tf variables have to be dependent on the
    # bases adding op, since tf variables that they depends on have been
    # assigned new values. Through this way, the assignment will be reflected in
    # a single session run.
    with tf.control_dependencies([bases_adding]):
      self._build_public_variables()

  def _build_bases_adding(self, num_bases_to_add, bases_to_sample=None):
    """Build operation that adds bases.

    Args:
      num_bases_to_add: number of bases to add to mean and covariance.
      bases_to_sample: rank-2 tensor or array, to sample from for
        initialization.
    Returns:
      tf operation.
    """
    num_mean_bases_new = tf.minimum(self._num_mean_bases + num_bases_to_add,
                                    self._mean_bases.shape[0])
    num_cov_bases_new = tf.minimum(self._num_cov_bases + num_bases_to_add,
                                   self._cov_bases.shape[0])
    # Decouple bases if mean and covariance will have different number of bases,
    # due to their different max number.
    bases_decoupling = tf.cond(
        tf.logical_and(
            tf.not_equal(num_mean_bases_new, num_cov_bases_new),
            self._is_coupled), self._build_bases_decoupling, lambda: tf.group())

    # Decoupling is done first.
    with tf.control_dependencies([bases_decoupling]):
      # Sample bases to initialize the newly added bases.
      bases_init = None
      if bases_to_sample is not None:
        bases_init = self._build_bases_sampling(bases_to_sample,
                                                num_bases_to_add)
      mean_bases_adding = tf.cond(
          tf.greater(num_mean_bases_new, self._num_mean_bases),
          lambda: self._build_bases_adding_worker(self._num_mean_bases,
                                                  num_mean_bases_new,
                                                  self._mean_bases,
                                                  self._mean_stats,
                                                  bases_init),
          lambda: tf.group())
      cov_bases_adding = tf.cond(
          tf.logical_and(
              tf.greater(num_cov_bases_new, self._num_cov_bases),
              tf.logical_not(self._is_coupled)),
          lambda: self._build_bases_adding_worker(self._num_cov_bases,
                                                  num_cov_bases_new,
                                                  self._cov_bases,
                                                  self._cov_stats,
                                                  bases_init),
          lambda: tf.group())
      num_cov_bases = tf.cond(
          self._is_coupled,
          lambda: tf.group(tf.assign(self._num_cov_bases, num_cov_bases_new)),
          lambda: tf.group())
      mean_bases_adding = tf.Print(mean_bases_adding, [self._num_mean_bases],
                                   'number of mean bases: ')
      num_cov_bases = tf.Print(num_cov_bases, [self._num_cov_bases],
                               'number of cov bases: ')

    return tf.group(mean_bases_adding, cov_bases_adding, num_cov_bases)

  # pylint: enable=unnecessary-lambda
  # pylint: enable=g-long-lambda

  def _build_bases_sampling(self, bases_to_sample, num_bases_to_sample):
    """Samples bases.

    Assuming the first k bases are the same with different num_bases_to_sample.

    Args:
      bases_to_sample: rank-2 tensor or array to sample bases from.
      num_bases_to_sample: number of bases to sample.
    Returns:
      rank-2 tensor.
    """
    bases_to_sample = tf.convert_to_tensor(
        bases_to_sample, dtype=utils.tf_float)
    idx = tf.random_uniform(
        shape=[num_bases_to_sample],
        maxval=tf.cast(tf.shape(bases_to_sample)[0], utils.tf_int),
        dtype=utils.tf_int)
    return tf.gather(bases_to_sample, idx)

  def _build_bases_decoupling(self):
    """Build operation that decouples bases."""
    bases_assign = tf.scatter_update(self._cov_bases,
                                     tf.range(self._num_cov_bases),
                                     self._mean_bases[:self._num_cov_bases])
    log_coeff_assign = tf.scatter_update(
        self._cov_log_coeff, tf.range(self._num_cov_bases),
        self._mean_log_coeff[:self._num_cov_bases])
    is_coupled_assign = tf.assign(self._is_coupled, False)
    bases_assign = tf.Print(bases_assign, [bases_assign], 'bases are decoupled')
    return tf.group(bases_assign, log_coeff_assign, is_coupled_assign)

  def _build_bases_adding_worker(self, num_bases, num_bases_new, bases, stats,
                                 bases_init):
    """Build operation that adds bases for mean or covariance.

    Args:
      num_bases: rank-0 tensor.
      num_bases_new: rank-0 tensor, new number of bases.
      bases: rank-2 tensor, private.
      stats: rank-0 tensor, private.
      bases_init: rank-2 tensor.
    Returns:
      tf operation.
    """
    stats_normalizer_new = self._build_stats_normalizer(num_bases_new, stats)
    stats_normalizer = self._build_stats_normalizer(num_bases, stats)
    stats_assign = tf.assign(stats,
                             stats * stats_normalizer_new / stats_normalizer)
    bases_assign = tf.group()
    if bases_init is not None:
      bases_assign = tf.scatter_update(bases, tf.range(
          num_bases, num_bases_new), bases_init[:num_bases_new - num_bases])
    # Now it's time to update the number of bases.
    with tf.control_dependencies([bases_assign, stats_assign]):
      num_bases = tf.assign(num_bases, num_bases_new)
    return tf.group(num_bases)

  def build_summaries(self):
    tf.summary.scalar(self._num_mean_bases.name, self._num_mean_bases)
    tf.summary.scalar(self._num_cov_bases.name, self._num_cov_bases)


text:
package com.google.api.codegen.transformer.py;

import com.google.api.codegen.config.*;
import com.google.api.codegen.metacode.InitCodeLineType;
import com.google.api.codegen.metacode.InitCodeNode;
import com.google.api.codegen.transformer.*;
import com.google.api.codegen.util.TypeAlias;
import com.google.api.codegen.util.py.PythonTypeTable;
import com.google.api.codegen.viewmodel.ImportFileView;
import com.google.api.codegen.viewmodel.ImportSectionView;
import com.google.api.codegen.viewmodel.ImportTypeView;
import com.google.api.tools.framework.model.Interface;
import com.google.api.tools.framework.model.MessageType;
import com.google.api.tools.framework.model.Model;
import com.google.api.tools.framework.model.ProtoFile;
import com.google.api.tools.framework.model.SymbolTable;
import com.google.api.tools.framework.model.TypeRef;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/* Creates the import sections of GAPIC generated code for Python. */
public class PythonImportSectionTransformer implements ImportSectionTransformer {

  @Override
  public ImportSectionView generateImportSection(TransformationContext context, String className) {
    InterfaceContext interfaceContext = (InterfaceContext) context;
    return ImportSectionView.newBuilder()
        .standardImports(generateFileHeaderStandardImports(interfaceContext))
        .externalImports(generateFileHeaderExternalImports(interfaceContext))
        .appImports(generateMainAppImports(interfaceContext))
        .build();
  }

  @Override
  public ImportSectionView generateImportSection(
      MethodContext context, Iterable<InitCodeNode> specItemNodes) {
    return ImportSectionView.newBuilder()
        .appImports(generateInitCodeAppImports(((GapicMethodContext) context), specItemNodes))
        .build();
  }

  public ImportSectionView generateTestImportSection(GapicInterfaceContext context) {
    return ImportSectionView.newBuilder()
        .standardImports(generateTestStandardImports())
        .externalImports(generateTestExternalImports(context))
        .appImports(generateTestAppImports(context))
        .build();
  }

  public ImportSectionView generateSmokeTestImportSection(
      GapicInterfaceContext context, boolean requireProjectId) {
    return ImportSectionView.newBuilder()
        .standardImports(generateSmokeTestStandardImports(requireProjectId))
        .externalImports(ImmutableList.of())
        .appImports(generateTestAppImports(context))
        .build();
  }

  public ImportSectionView generateGrpcTransportImportSection(GapicInterfaceContext context) {
    return ImportSectionView.newBuilder()
        .standardImports(ImmutableList.of())
        .externalImports(generateGrpctransportExternalImports(context))
        .appImports(generateGrpcTransportAppImports(context))
        .build();
  }

  private List<ImportFileView> generateGrpctransportExternalImports(GapicInterfaceContext context) {
    ImmutableList.Builder<ImportFileView> externalImports = ImmutableList.builder();
    externalImports.add(createImport("google.api_core.grpc_helpers"));

    if (context.getInterfaceConfig().hasLongRunningOperations()) {
      externalImports.add(createImport("google.api_core.operations_v1"));
    }
    return externalImports.build();
  }

  private List<ImportFileView> generateGrpcTransportAppImports(GapicInterfaceContext context) {
    Map<String, InterfaceModel> interfaces = new TreeMap<>();
    for (MethodModel method : context.getSupportedMethods()) {
      InterfaceModel targetInterface = context.asRequestMethodContext(method).getTargetInterface();
      interfaces.put(targetInterface.getFullName(), targetInterface);
    }
    ModelTypeTable typeTable = context.getImportTypeTable().cloneEmpty();
    for (InterfaceModel targetInterface : interfaces.values()) {
      context.getNamer().getAndSaveNicknameForGrpcClientTypeName(typeTable, targetInterface);
    }
    return generateFileHeaderAppImports(typeTable.getImports());
  }

  private List<ImportFileView> generateFileHeaderStandardImports(InterfaceContext context) {
    ImmutableList.Builder<ImportFileView> imports = ImmutableList.builder();
    if (context.getInterfaceConfig().hasPageStreamingMethods()) {
      imports.add(createImport("functools"));
    }
    imports.add(createImport("pkg_resources"));
    imports.add(createImport("warnings"));
    return imports.build();
  }

  private List<ImportFileView> generateFileHeaderExternalImports(InterfaceContext context) {
    List<ImportFileView> imports = new ArrayList<>();
    imports.add(createImport("grpc"));
    imports.add(createImport("google.oauth2", "service_account"));
    imports.add(createImport("google.api_core.grpc_helpers"));
    imports.add(createImport("google.api_core.gapic_v1.client_info"));
    imports.add(createImport("google.api_core.client_options"));
    imports.add(createImport("google.api_core.gapic_v1.config"));
    imports.add(createImport("google.api_core.gapic_v1.method"));

    if (hasRequestHeaderParams((GapicInterfaceContext) context)) {
      imports.add(createImport("google.api_core.gapic_v1.routing_header"));
    }

    if (context.getInterfaceConfig().hasLongRunningOperations()) {
      imports.add(createImport("google.api_core.operations_v1"));
      imports.add(createImport("google.api_core.operation"));
    }

    if (context.getInterfaceConfig().hasPageStreamingMethods()) {
      imports.add(createImport("google.api_core.page_iterator"));
    }

    if (!(new PathTemplateTransformer().generateFormatResourceFunctions(context)).isEmpty()) {
      imports.add(createImport("google.api_core.path_template"));
    }

    if (hasOneOf(context)) {
      imports.add(createImport("google.api_core.protobuf_helpers"));
    }

    Collections.sort(imports, importFileViewComparator());
    return imports;
  }

  private boolean hasRequestHeaderParams(GapicInterfaceContext context) {
    return context
        .getInterfaceConfig()
        .getMethodConfigs()
        .stream()
        .anyMatch(config -> config.getHeaderRequestParams().iterator().hasNext());
  }

  private boolean hasOneOf(InterfaceContext context) {
    return context
        .getInterfaceConfig()
        .getMethodConfigs()
        .stream()
        .anyMatch(config -> !config.getOneofNames(context.getNamer()).isEmpty());
  }

  private List<ImportFileView> generateMainAppImports(InterfaceContext context) {
    List<ImportFileView> imports =
        generateFileHeaderAppImports(context.getImportTypeTable().getImports());
    SurfaceNamer namer = context.getNamer();
    imports.add(
        createImport(
            namer.getPackageName(), namer.getClientConfigName(context.getInterfaceConfig())));
    imports.add(
        createImport(
            namer.getPackageName() + ".transports",
            namer.getGrpcTransportImportName(context.getInterfaceConfig())));
    Collections.sort(imports, importFileViewComparator());
    return imports;
  }

  private List<ImportFileView> generateFileHeaderAppImports(Map<String, TypeAlias> typeImports) {
    List<ImportFileView> appImports = new ArrayList<>();
    for (Map.Entry<String, TypeAlias> entry : typeImports.entrySet()) {
      appImports.add(generateAppImport(entry.getKey(), entry.getValue().getNickname()));
    }
    Collections.sort(appImports, importFileViewComparator());
    return appImports;
  }

  private List<ImportFileView> generateInitCodeAppImports(
      GapicMethodContext context, Iterable<InitCodeNode> specItemNodes) {
    ImmutableList.Builder<ImportFileView> imports = ImmutableList.builder();
    imports
        .add(generateApiImport(context.getNamer()))
        .addAll(generateProtoImports(context, specItemNodes));
    boolean needIOUtility =
        Streams.stream(specItemNodes)
            .anyMatch(node -> node.getLineType() == InitCodeLineType.ReadFileInitLine);
    if (needIOUtility) {
      imports.add(createImport("io"));
    }
    return imports.build();
  }

  private ImportFileView generateApiImport(SurfaceNamer namer) {
    String moduleName = namer.getTopLevelNamespace();
    String attributeName = namer.getApiWrapperModuleName();
    if (Strings.isNullOrEmpty(moduleName)) {
      return createImport(attributeName);
    }
    return createImport(moduleName, attributeName);
  }

  private List<ImportFileView> generateProtoImports(
      GapicMethodContext context, Iterable<InitCodeNode> specItemNodes) {
    Set<ImportFileView> protoImports = new TreeSet<>(importFileViewComparator());
    for (InitCodeNode item : specItemNodes) {
      TypeModel type = item.getType();
      // Exclude map entry types
      // Exclude message types since the samples show the use of dicts rather than protobufs.
      if (type.isEnum()) {
        protoImports.add(
            createImport(context.getNamer().getVersionedDirectoryNamespace(), "enums"));
      }
    }
    return new ArrayList<>(protoImports);
  }

  private List<ImportFileView> generateTestStandardImports() {
    ImmutableList.Builder<ImportFileView> imports = ImmutableList.builder();
    imports.add(createImport("mock"));
    imports.add(createImport("pytest"));
    return imports.build();
  }

  private List<ImportFileView> generateSmokeTestStandardImports(boolean requireProjectId) {
    ImmutableList.Builder<ImportFileView> imports = ImmutableList.builder();
    if (requireProjectId) {
      imports.add(createImport("os"));
    }
    imports.add(createImport("time"));
    return imports.build();
  }

  private List<ImportFileView> generateTestExternalImports(GapicInterfaceContext context) {
    ImmutableList.Builder<ImportFileView> externalImports = ImmutableList.builder();
    if (context.getInterfaceConfig().hasLongRunningOperations()) {
      externalImports.add(createImport("google.rpc", "status_pb2"));
    }
    return externalImports.build();
  }

  private List<ImportFileView> generateTestAppImports(GapicInterfaceContext context) {
    Set<ImportFileView> appImports = new TreeSet<>(importFileViewComparator());
    for (Map.Entry<String, TypeAlias> entry :
        context.getImportTypeTable().getImports().entrySet()) {
      String fullName = entry.getKey();
      if (fullName.startsWith(context.getNamer().getTestPackageName() + ".enums")) {
        appImports.add(createImport(context.getNamer().getVersionedDirectoryNamespace(), "enums"));
      } else {
        appImports.add(generateAppImport(entry.getKey(), entry.getValue().getNickname()));
      }
    }
    appImports.add(generateApiImport(context.getNamer()));
    return new ArrayList<>(appImports);
  }

  /**
   * Orders the imports by:
   *
   *

   *
number of attributes (least to most)
   *
module name (A-Z)
   *
attribute name (A-Z)
   *

   */
  private Comparator<ImportFileView> importFileViewComparator() {
    return new Comparator<ImportFileView>() {
      @Override
      public int compare(ImportFileView o1, ImportFileView o2) {
        if (o1.types().size() != o2.types().size()) {
          return o2.types().size() - o1.types().size();
        }

        if (!o1.moduleName().equals(o2.moduleName())) {
          return o1.moduleName().compareTo(o2.moduleName());
        }

        for (int i = 0; i < o1.types().size(); ++i) {
          String attributeName1 = o1.types().get(i).fullName();
          String attributeName2 = o2.types().get(i).fullName();

          if (!attributeName1.equals(attributeName2)) {
            return attributeName1.compareTo(attributeName2);
          }
        }

        return 0;
      }
    };
  }

  /**
   * Generates an import from the fullName and the nickname of a type.
   *
   *
Handles the following cases:
   *
   *

   *
Module only -- generateAppImport("foo.Bar", "foo.Bar") => import foo
   *
Module and attribute -- generateAppImport("foo.bar.Baz", "bar.Baz") => from foo import
   *       bar
   *
Module, attribute, local -- generateAppImport("foo.bar.Baz", "qux.Baz") => from * foo
   *       import bar as qux
   *

   */
  private ImportFileView generateAppImport(String fullName, String nickname) {
    int nicknameDottedClassIndex = nickname.indexOf(".");
    String localName = nickname.substring(0, nicknameDottedClassIndex);

    if (fullName.equals(nickname)) {
      return createImport(localName);
    }

    if (fullName.endsWith(nickname)) {
      String moduleName = fullName.substring(0, fullName.length() - nickname.length() - 1);
      return createImport(moduleName, localName);
    }

    int fullNameDottedClassIndex = fullName.length() - nickname.length() + nicknameDottedClassIndex;
    int dottedAttributeIndex = fullName.lastIndexOf(".", fullNameDottedClassIndex - 1);
    String moduleName = fullName.substring(0, dottedAttributeIndex);
    String attributeName = fullName.substring(dottedAttributeIndex + 1, fullNameDottedClassIndex);
    return createImport(moduleName, attributeName, localName);
  }

  private ImportFileView createImport(String moduleName) {
    return ImportFileView.newBuilder()
        .moduleName(moduleName)
        .types(ImmutableList.<ImportTypeView>of())
        .build();
  }

  private ImportFileView createImport(String moduleName, String attributeName) {
    ImportTypeView typeImport =
        ImportTypeView.newBuilder().fullName(attributeName).nickname("").build();
    return ImportFileView.newBuilder()
        .moduleName(moduleName)
        .types(ImmutableList.of(typeImport))
        .build();
  }

  private ImportFileView createImport(String moduleName, String attributeName, String localName) {
    ImportTypeView typeImport =
        ImportTypeView.newBuilder().fullName(attributeName).nickname(localName).build();
    return ImportFileView.newBuilder()
        .moduleName(moduleName)
        .types(ImmutableList.of(typeImport))
        .build();
  }

  public ImportSectionView generateTypesImportSection(
      Model model, GapicProductConfig productConfig) {
    ImportSectionView.Builder importView = generateTypesProtoImports(model, productConfig);
    return importView
        .externalImports(generateTypesExternalImports())
        .standardImports(generateTypesStandardImports())
        .build();
  }

  private ImportSectionView.Builder generateTypesProtoImports(
      Model model, GapicProductConfig productConfig) {
    ImportSectionView.Builder importView = ImportSectionView.newBuilder();

    ModelTypeTable allTypeTable = emptyTypeTable(productConfig);

    // Imports from the same API client library package.
    Set<ImportFileView> localImports = new TreeSet<>(importFileViewComparator());

    // Shared imports, e.g. protobuf imports.
    Set<ImportFileView> sharedImports = new TreeSet<>(importFileViewComparator());

    // All imports.
    Set<ImportFileView> appImports = new TreeSet<>(importFileViewComparator());
    Set<String> localImportNames = new HashSet<>();
    Set<String> sharedImportNames = new HashSet<>();

    SymbolTable symbolTable = model.getSymbolTable();
    Collection<ProtoFile> localImportFiles =
        productConfig
            .getInterfaceConfigMap()
            .keySet()
            .stream()
            .map(symbolTable::lookupInterface)
            .filter(Objects::nonNull)
            .map(Interface::getFile)
            .collect(ImmutableSet.toImmutableSet());

    Iterable<ProtoFile> allFiles = model.reachable(model.getFiles());

    // Skip API interfaces excluded from interface configs.
    Collection<ProtoFile> skippedFiles =
        Streams.stream(allFiles)
            .filter(ProtoFile::isSource)
            .flatMap(f -> f.getInterfaces().stream())
            .filter(Interface::isReachable)
            .filter(i -> productConfig.getInterfaceConfig(i) == null)
            .map(Interface::getFile)
            .collect(ImmutableSet.toImmutableSet());

    // Can't use Collection#removeAll since allFiles is an Iterable.
    Iterable<ProtoFile> protoFileDependencies =
        Streams.stream(allFiles)
            .filter(f -> !skippedFiles.contains(f))
            .collect(ImmutableSet.toImmutableSet());

    // Save proto file import names to the type table for disambiguation.
    populateTypeTable(
        protoFileDependencies, allTypeTable, localImportNames, sharedImportNames, localImportFiles);

    // Get disambiguated imports.
    for (Map.Entry<String, TypeAlias> entry : allTypeTable.getImports().entrySet()) {
      String importFullName = entry.getKey();
      String nickName = entry.getValue().getNickname();
      appImports.add(generateAppImport(importFullName, nickName));
      if (localImportNames.contains(importFullName)) {
        localImports.add(generateAppImport(importFullName, nickName));
      } else if (sharedImportNames.contains(importFullName)) {
        sharedImports.add(generateAppImport(importFullName, nickName));
      }
    }
    importView.localImports(ImmutableList.copyOf(localImports));
    importView.sharedImports(ImmutableList.copyOf(sharedImports));
    importView.appImports(ImmutableList.copyOf(appImports));

    return importView;
  }

  private ModelTypeTable emptyTypeTable(GapicProductConfig productConfig) {
    return new ModelTypeTable(
        new PythonTypeTable(productConfig.getPackageName()),
        new PythonModelTypeNameConverter(productConfig.getPackageName()));
  }

  private List<ImportFileView> generateTypesExternalImports() {
    return ImmutableList.of(createImport("google.api_core.protobuf_helpers", "get_messages"));
  }

  private List<ImportFileView> generateTypesStandardImports() {
    return ImmutableList.of(createImport("__future__", "absolute_import"), createImport("sys"));
  }

  /**
   * For each ProtoFile dependency, put its TypeAlias in allTypeTable; and if it is a local import,
   * then put it in the given localImportNames, otherwise if it is a shared import, but it in the
   * given sharedImportNames.
   */
  private void populateTypeTable(
      Iterable<ProtoFile> protoFileDependencies,
      ModelTypeTable allTypeTable,
      Set<String> localImportNames,
      Set<String> sharedImportNames,
      Collection<ProtoFile> localImportFiles) {
    for (ProtoFile protoFile : protoFileDependencies) {
      // For python, adding a single message from the proto file to the type table will populate
      // the type table with the correct imports.
      ImmutableList<MessageType> messages = protoFile.getMessages();
      if (!messages.isEmpty()) {
        TypeRef typeRef = TypeRef.of(messages.get(0));
        allTypeTable.getAndSaveNicknameFor(typeRef);

        if (localImportFiles
            .stream()
            .anyMatch(f -> f.getFullName().equals(protoFile.getFullName()))) {
          localImportNames.add(allTypeTable.getFullNameFor(typeRef));
        } else {
          sharedImportNames.add(allTypeTable.getFullNameFor(typeRef));
        }
      }
    }
  }

  public ImportSectionView generateVersionedInitImportSection(
      ApiModel apiModel,
      GapicProductConfig productConfig,
      SurfaceNamer namer,
      boolean packageHasEnums) {

    List<ImportFileView> imports = new ArrayList<>();
    imports.addAll(generateAbsoluteImportImportSection());
    imports.add(createImport("sys"));
    imports.add(createImport("warnings"));
    Collections.sort(imports, importFileViewComparator());
    return ImportSectionView.newBuilder()
        .appImports(
            generateVersionedInitAppImports(apiModel, productConfig, namer, packageHasEnums))
        .standardImports(imports)
        .build();
  }

  private List<ImportFileView> generateAbsoluteImportImportSection() {
    return ImmutableList.of(createImport("__future__", "absolute_import"));
  }

  private List<ImportFileView> generateVersionedInitAppImports(
      ApiModel apiModel,
      GapicProductConfig productConfig,
      SurfaceNamer namer,
      boolean packageHasEnums) {
    Set<ImportFileView> imports = new TreeSet<>(importFileViewComparator());
    for (InterfaceModel apiInterface : apiModel.getInterfaces(productConfig)) {
      InterfaceConfig interfaceConfig = productConfig.getInterfaceConfig(apiInterface);
      if (interfaceConfig == null) {
        continue;
      }

      imports.add(
          createImport(
              productConfig.getPackageName(), namer.getApiWrapperVariableName(interfaceConfig)));
    }

    if (packageHasEnums) {
      imports.add(createImport(productConfig.getPackageName(), "enums"));
    }
    imports.add(createImport(namer.getVersionedDirectoryNamespace(), "types"));
    return ImmutableList.copyOf(imports);
  }

  public ImportSectionView generateTopLeveEntryPointImportSection(
      ApiModel apiModel,
      GapicProductConfig productConfig,
      SurfaceNamer namer,
      boolean packageHasEnums) {
    return ImportSectionView.newBuilder()
        .appImports(
            generateTopLevelEntryPointAppImports(apiModel, productConfig, namer, packageHasEnums))
        .standardImports(generateAbsoluteImportImportSection())
        .build();
  }

  private List<ImportFileView> generateTopLevelEntryPointAppImports(
      ApiModel apiModel,
      GapicProductConfig productConfig,
      SurfaceNamer namer,
      boolean packageHasEnums) {
    Set<ImportFileView> imports = new TreeSet<>(importFileViewComparator());
    for (InterfaceModel apiInterface : apiModel.getInterfaces(productConfig)) {
      InterfaceConfig interfaceConfig = productConfig.getInterfaceConfig(apiInterface);
      if (interfaceConfig == null) {
        continue;
      }

      imports.add(
          createImport(
              namer.getVersionedDirectoryNamespace(),
              namer.getApiWrapperClassName(interfaceConfig)));
    }
    if (packageHasEnums) {
      imports.add(createImport(namer.getVersionedDirectoryNamespace(), "enums"));
    }
    imports.add(createImport(namer.getVersionedDirectoryNamespace(), "types"));
    return ImmutableList.copyOf(imports);
  }

  public ImportSectionView generateNoxImportSection() {
    return ImportSectionView.newBuilder()
        .appImports(ImmutableList.<ImportFileView>of())
        .externalImports(generateNoxExternalImports())
        .standardImports(generateNoxStandardImports())
        .build();
  }

  private List<ImportFileView> generateNoxExternalImports() {
    return ImmutableList.of(createImport("nox"));
  }

  private List<ImportFileView> generateNoxStandardImports() {
    List<ImportFileView> imports = new ArrayList<>();
    imports.addAll(generateAbsoluteImportImportSection());
    imports.add(createImport("os"));
    Collections.sort(imports, importFileViewComparator());
    return imports;
  }
}


text:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
<!-- Spanish language-Team URL: http://www.transifex.com/projects/p/xbmc-addons/language/es/ -->
<!-- Report language file syntax bugs at: alanwww1@xbmc.org -->

<strings>
    <string id="30010">XBMC Backup</string>
    <string id="30011">General</string>
    <string id="30012">Selección de archivo</string>
    <string id="30013">Planificando</string>
    <string id="30016">Backup</string>
    <string id="30017">Restaurar</string>
    <string id="30018">Examinar ruta</string>
    <string id="30019">Escribir ruta</string>
    <string id="30020">Examinar ruta remota</string>
    <string id="30021">Nombre de directorio de respaldo</string>
    <string id="30022">Pantalla de progreso</string>
    <string id="30023">Modo</string>
    <string id="30024">Escribir directorio remoto</string>
    <string id="30025">Tipo de Ruta Remota</string>
    <string id="30026">Copias de seguridad a mantener (0 para todas)</string>
    <string id="30027">Dropbox</string>
    <string id="30028">Clave de Dropbox</string>
    <string id="30029">Secreto de Dropbox</string>
    <string id="30030">Addons del usuario</string>
    <string id="30031">Addon Data</string>
    <string id="30032">Base de Datos</string>
    <string id="30033">Lista de reproducción</string>
    <string id="30034">Thumbnails/Fanart</string>
    <string id="30035">Ficheros de configuración</string>
    <string id="30036">Directorio Personalizado 1</string>
    <string id="30037">Directorio Personalizado 2</string>
    <string id="30038">Opciones avanzadas detectadas</string>
    <string id="30039">Primero debería ser restaurado el archivo advancedsettings</string>
    <string id="30040">Selecciona SI para restaurar este archivo y reiniciar XBMC</string>
    <string id="30041">Selecciona NO para continuar</string>
    <string id="30042">Reanudar restauración</string>
    <string id="30043">XBMC Backup ha detectado una restauración sin terminar</string>
    <string id="30044">¿Desea continuar?</string>
    <string id="30045">Error: La ruta Remota no existe</string>
    <string id="30046">Empezando</string>
    <string id="30047">Directorio Local</string>
    <string id="30048">Directorio Remoto</string>
    <string id="30049">Obteniendo lista de fichero</string>
    <string id="30050">¡Ruta Remota ya existe - puede haber ficheros antiguos en ella!</string>
    <string id="30051">Creando lista de ficheros</string>
    <string id="30052">Escribiendo fichero</string>
    <string id="30053">Empezando copia de seguridad planificada</string>
    <string id="30054">Eliminar copia de seguridad</string>
    <string id="30056">Autoriza la URL en Dropbox</string>
    <string id="30057">Presiona OK cuando se haya autorizado</string>
    <string id="30058">Código de Desarrollador Dropbox necesario</string>
    <string id="30059">Visita https://www.dropbox.com/developers</string>
    <string id="30060">Habilitar Planificador</string>
    <string id="30061">Planificador</string>
    <string id="30062">Hora del Día</string>
    <string id="30063">Día de la Semana</string>
    <string id="30064">Cron Schedule</string>
    <string id="30065">Domingo</string>
    <string id="30066">Lunes</string>
    <string id="30067">Martes</string>
    <string id="30068">Miércoles</string>
    <string id="30069">Jueves</string>
    <string id="30070">Viernes</string>
    <string id="30071">Sábado</string>
    <string id="30072">Todos los días</string>
    <string id="30073">Todas las semanas</string>
    <string id="30074">Primer día de la semana</string>
    <string id="30075">Planificador Custom</string>
    <string id="30076">Apagar tras realizar la copia de seguridad</string>
    <string id="30077">Reiniciar XBMC</string>
    <string id="30078">Debería reiniciar XBMC para continuar</string>
    <string id="30079">Hoy mismo</string>
    <string id="30080">Perfiles</string>
    <string id="30081">Planificador se ejecutará de nuevo en</string>
    <string id="30082">Barra de progreso</string>
    <string id="30083">Fondo de la barra de progreso</string>
    <string id="30084">Ninguno (Silencio)</string>
    <string id="30085">Advertencia de versión </string>
    <string id="30086">Esta versión de XBMC es diferente a la que se utilizó para crear el archivo</string>
</strings>


text:
<?php

declare(strict_types=1);

return [
    'meta' => [
        'title' => 'Namespaced function call imported with an aliased use statement in the global scope',
        // Default values. If not specified will be the one used
        'prefix' => 'Humbug',

        'expose-global-constants' => false,
        'expose-global-classes' => false,
        'expose-global-functions' => false,
        'expose-namespaces' => [],
        'expose-constants' => [],
        'expose-classes' => [],
        'expose-functions' => [],

        'exclude-namespaces' => [],
        'exclude-constants' => [],
        'exclude-classes' => [],
        'exclude-functions' => [],

        'expected-recorded-classes' => [],
        'expected-recorded-functions' => [],
    ],

    'Namespaced function call imported with a partial use statement in the global scope' => <<<'PHP'
    <?php

    namespace {
        class Foo {}
    }

    namespace Foo {
        function main() {}
    }

    namespace {
        use Foo as X;

        X\main();
    }
    ----
    <?php

    namespace Humbug;

    class Foo
    {
    }
    namespace Humbug\Foo;

    function main()
    {
    }
    namespace Humbug;

    use Humbug\Foo as X;
    X\main();

    PHP,

    'FQ namespaced function call imported with a partial use statement in the global scope' => <<<'PHP'
    <?php

    namespace {
        class Foo {}
    }

    namespace X {
        function main() {}
    }

    namespace {
        use Foo as X;

        \X\main();
    }
    ----
    <?php

    namespace Humbug;

    class Foo
    {
    }
    namespace Humbug\X;

    function main()
    {
    }
    namespace Humbug;

    use Humbug\Foo as X;
    \Humbug\X\main();

    PHP,

    'Exposed namespaced function call imported with a partial use statement in the global scope' => [
        'expose-functions' => ['Foo\main'],
        'expected-recorded-functions' => [
            ['Foo\main', 'Humbug\Foo\main'],
        ],
        'payload' => <<<'PHP'
        <?php

        namespace {
            class Foo {}
        }

        namespace Foo {
            function main() {}
        }

        namespace {
            use Foo as X;

            X\main();
        }
        ----
        <?php

        namespace Humbug;

        class Foo
        {
        }
        namespace Humbug\Foo;

        function main()
        {
        }
        namespace Humbug;

        use Humbug\Foo as X;
        X\main();

        PHP,
    ],
];


text:
.class Lcom/android/server/am/ActivityManagerService$PendingActivityLaunch;
.super Ljava/lang/Object;
.source "ActivityManagerService.java"

# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
    value = Lcom/android/server/am/ActivityManagerService;
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x8
    name = "PendingActivityLaunch"
.end annotation

# instance fields
.field final r:Lcom/android/server/am/ActivityRecord;

.field final sourceRecord:Lcom/android/server/am/ActivityRecord;

.field final stack:Lcom/android/server/am/ActivityStack;

.field final startFlags:I

# direct methods
.method constructor <init>(Lcom/android/server/am/ActivityRecord;Lcom/android/server/am/ActivityRecord;ILcom/android/server/am/ActivityStack;)V
    .registers 5
    .param p1    # Lcom/android/server/am/ActivityRecord;
    .param p2    # Lcom/android/server/am/ActivityRecord;
    .param p3    # I
    .param p4    # Lcom/android/server/am/ActivityStack;

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    iput-object p1, p0, Lcom/android/server/am/ActivityManagerService$PendingActivityLaunch;->r:Lcom/android/server/am/ActivityRecord;

    iput-object p2, p0, Lcom/android/server/am/ActivityManagerService$PendingActivityLaunch;->sourceRecord:Lcom/android/server/am/ActivityRecord;

    iput p3, p0, Lcom/android/server/am/ActivityManagerService$PendingActivityLaunch;->startFlags:I

    iput-object p4, p0, Lcom/android/server/am/ActivityManagerService$PendingActivityLaunch;->stack:Lcom/android/server/am/ActivityStack;

    return-void
.end method