text:
/*!
** @file ADCs.c
** @version 01.00
** @brief
**         This device "ADC" implements an A/D converter,
**         its control methods and interrupt/event handling procedure.
*/
/*!
**  @addtogroup ADCs_module ADCs module documentation
**  @{
*/

/* MODULE ADCs. */

#include "Events.h"
#include "ADCs.h"

#ifdef __cplusplus
extern "C" {
#endif
#define STOP            0x00U          /* STOP state           */
#define MEASURE         0x01U          /* MESURE state         */
#define CONTINUOUS      0x02U          /* CONTINUOUS state     */
#define SINGLE          0x03U          /* SINGLE state         */
#define CALIBRATING     0x04U          /* CALIBRATING state    */

static volatile byte SumChan;          /* Counter of measured channels */
static volatile byte ModeFlg;          /* Current state of device */
LDD_TDeviceData *AdcLdd1_DeviceDataPtr; /* Device data pointer */
/* Sample group configuration */
static LDD_ADC_TSample SampleGroup[ADCs_SAMPLE_GROUP_SIZE];
static const  byte Table[3] = {0x01U,0x02U,0x04U};  /* Table of mask constants */
/* Measure multiple channels flags  */
/* Temporary buffer for converting results */
volatile byte ADCs_OutV[ADCs_SAMPLE_GROUP_SIZE]; /* Sum of measured values */
/* Calibration in progress flag */
static volatile byte OutFlg;           /* Measurement finish flag */

/*
** ===================================================================
**     Method      :  ClrSumV (component ADC)
**
**     Description :
**         The method clears the internal buffers used to store sum of a
**         number of last conversions.
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
static void ClrSumV(void)
{
  ADCs_OutV[0] = 0U;                   /* Set variable for storing measured values to 0 */
  ADCs_OutV[1] = 0U;                   /* Set variable for storing measured values to 0 */
  ADCs_OutV[2] = 0U;                   /* Set variable for storing measured values to 0 */
}

/*
** ===================================================================
**     Method      :  ADCs_HWEnDi (component ADC)
**
**     Description :
**         Enables or disables the peripheral(s) associated with the
**         component. The method is called automatically as a part of the
**         Enable and Disable methods and several internal methods.
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
void ADCs_HWEnDi(void)
{
  if (ModeFlg) {                       /* Start or stop measurement? */
    if (ModeFlg != SINGLE) {
      OutFlg = 0U;                     /* Output values aren't available */
      SumChan = 0U;                    /* Set the counter of measured channels to 0 */
      ClrSumV();                       /* Clear measured values */
      SampleGroup[0].ChannelIdx = 0U;
    }
    else {
      OutFlg &= (byte)(~(byte)Table[SumChan]); /* Output value isn't available */
      ADCs_OutV[SumChan] = 0U;         /* Set variable for storing measured values to 0 */
      SampleGroup[0].ChannelIdx = SumChan;
    }
    (void)AdcLdd1_CreateSampleGroup(AdcLdd1_DeviceDataPtr, (LDD_ADC_TSample *)SampleGroup, 1U); /* Configure sample group */
    (void)AdcLdd1_StartSingleMeasurement(AdcLdd1_DeviceDataPtr);
  }
}

/*
** ===================================================================
**     Method      :  ADCs_Measure (component ADC)
*/
/*!
**     @brief
**         This method performs one measurement on all channels that
**         are set in the component inspector. (Note: If the [number of
**         conversions] is more than one the conversion of A/D channels
**         is performed specified number of times.)
**     @param
**         WaitForResult   - Wait for a result of a
**                           conversion. If [interrupt service] is
**                           disabled, A/D peripheral doesn't support
**                           measuring all channels at once or Autoscan
**                           mode property isn't enabled and at the same
**                           time the [number of channels] is greater
**                           than 1, then the WaitForResult parameter is
**                           ignored and the method waits for each
**                           result every time. If the [interrupt
**                           service] is disabled and a [number of
**                           conversions] is greater than 1, the
**                           parameter is ignored and the method also
**                           waits for each result every time.
**     @return
**                         - Error code, possible codes:
**                           ERR_OK - OK
**                           ERR_SPEED - This device does not work in
**                           the active speed mode
**                           ERR_DISABLED - Device is disabled
**                           ERR_BUSY - A conversion is already running
*/
/* ===================================================================*/
byte ADCs_Measure(bool WaitForResult)
{
  if (ModeFlg != STOP) {               /* Is the device in different mode than "stop"? */
    return ERR_BUSY;                   /* If yes then error */
  }
  ModeFlg = MEASURE;                   /* Set state of device to the measure mode */
  ADCs_HWEnDi();                       /* Enable the device */
  if (WaitForResult) {                 /* Is WaitForResult TRUE? */
    while (ModeFlg == MEASURE) {}      /* If yes then wait for end of measurement */
  }
  return ERR_OK;                       /* OK */
}

/*
** ===================================================================
**     Method      :  ADCs_MeasureChan (component ADC)
*/
/*!
**     @brief
**         This method performs measurement on one channel. (Note: If
**         the [number of conversions] is more than one the conversion
**         of the A/D channel is performed specified number of times.)
**     @param
**         WaitForResult   - Wait for a result of
**                           conversion. If the [interrupt service] is
**                           disabled and at the same time a [number of
**                           conversions] is greater than 1, the
**                           WaitForResult parameter is ignored and the
**                           method waits for each result every time.
**     @param
**         Channel         - Channel number. If only one
**                           channel in the component is set this
**                           parameter is ignored, because the parameter
**                           is set inside this method.
**     @return
**                         - Error code, possible codes:
**                           ERR_OK - OK
**                           ERR_SPEED - This device does not work in
**                           the active speed mode
**                           ERR_DISABLED - Device is disabled
**                           ERR_BUSY - A conversion is already running
**                           ERR_RANGE - Parameter "Channel" out of range
*/
/* ===================================================================*/
/*
The definition of method:
byte ADCs_MeasureChan(bool WaitForResult, byte Channel)
was optimised based on the current component setting. An appropriate macro has been defined
in the AD1.h to maintain API compatibility.
*/
byte ADCs_MeasureChan(bool WaitForResult,byte Channel)
{
  if (Channel >= 3U) {                 /* Is channel number greater than or equal to 3 */
    return ERR_RANGE;                  /* If yes then error */
  }
  if (ModeFlg != STOP) {               /* Is the device in different mode than "stop"? */
    return ERR_BUSY;                   /* If yes then error */
  }
  ModeFlg = SINGLE;                    /* Set state of device to the measure mode */
  SumChan = Channel;                   /* Set required channel */
  ADCs_HWEnDi();                       /* Enable the device */
  if (WaitForResult) {                 /* Is WaitForResult TRUE? */
    while (ModeFlg == SINGLE) {}       /* If yes then wait for end of measurement */
  }
  return ERR_OK;                       /* OK */
}

/*
** ===================================================================
**     Method      :  ADCs_GetChanValue (component ADC)
*/
/*!
**     @brief
**         Returns the last measured value of the required channel.
**         Format and width of the value is a native format of the A/D
**         converter.
**     @param
**         Channel         - Channel number. If only one
**                           channel in the component is set then this
**                           parameter is ignored.
**     @param
**         Value           - Pointer to the measured value. Data
**                           type is a byte, a word or an int. It
**                           depends on the supported modes, resolution,
**                           etc. of the AD converter. See the Version
**                           specific information for the current CPU in
**                           [General Info].
**     @return
**                         - Error code, possible codes:
**                           ERR_OK - OK
**                           ERR_SPEED - This device does not work in
**                           the active speed mode
**                           ERR_NOTAVAIL - Requested value not
**                           available
**                           ERR_RANGE - Parameter "Channel" out of
**                           range
**                           ERR_OVERRUN - External trigger overrun flag
**                           was detected after the last value(s) was
**                           obtained (for example by GetValue). This
**                           error may not be supported on some CPUs
**                           (see generated code).
*/
/* ===================================================================*/
/*
The definition of method:
byte ADCs_GetChanValue(byte Channel, void* Value)
was optimised based on the current component setting. An appropriate macro has been defined
in the AD1.h to maintain API compatibility.
*/
byte ADCs_GetChanValue(byte Channel, void* Value)
{
  if (Channel >= 3U) {                 /* Is channel number greater than or equal to 3 */
    return ERR_RANGE;                  /* If yes then error */
  }
  if ((OutFlg & Table[Channel]) == 0U) { /* Is output flag set? */
    return ERR_NOTAVAIL;               /* If no then error */
  }
  /* Copy value from temporary buffer */
  *(AdcLdd1_TResultData *)Value = ADCs_OutV[Channel];
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  ADCs_GetValue16 (component ADC)
*/
/*!
**     @brief
**         This method returns the last measured values of all channels.
**         Compared with [GetValue] method this method returns more
**         accurate result if the [number of conversions] is greater
**         than 1 and [AD resolution] is less than 16 bits. In addition,
**         the user code dependency on [AD resolution] is eliminated.
**     @param
**         Values          - Pointer to the array that contains
**                           the measured data.
**     @return
**                         - Error code, possible codes:
**                           ERR_OK - OK
**                           ERR_SPEED - This device does not work in
**                           the active speed mode
**                           ERR_NOTAVAIL - Requested value not
**                           available
**                           ERR_OVERRUN - External trigger overrun flag
**                           was detected after the last value(s) was
**                           obtained (for example by GetValue). This
**                           error may not be supported on some CPUs
**                           (see generated code).
*/
/* ===================================================================*/
byte ADCs_GetValue16(word *Values)
{
  if (OutFlg != 0x07U) {               /* Is output flag set? */
    return ERR_NOTAVAIL;               /* If no then error */
  }
  Values[0] = (word)(((word)(ADCs_OutV[0])) << 8U); /* Save measured values to the output buffer */
  Values[1] = (word)(((word)(ADCs_OutV[1])) << 8U); /* Save measured values to the output buffer */
  Values[2] = (word)(((word)(ADCs_OutV[2])) << 8U); /* Save measured values to the output buffer */
  return ERR_OK;                       /* OK */
}

/*
** ===================================================================
**     Method      :  ADCs_Calibrate (component ADC)
**     Description :
**         This method starts self calibration process. Calibration is
**         typically used to remove the effects of the gain and offset
**         from a specific reading.
**     Parameters  :
**         NAME            - DESCRIPTION
**         WaitForResult   - Wait for a result of
**                           calibration. If the <interrupt service> is
**                           disabled, the WaitForResult parameter is
**                           ignored and the method waits for
**                           calibration result every time.
**     Returns     :
**         ---             - Error code
**                           ERR_OK - OK
**                           ERR_BUSY - A conversion is already running
**                           ERR_SPEED - This device does not work in
**                           the active speed mode
**                           ERR_DISABLED - Device is disabled
**                           ERR_FAILED - Calibration hasn't been
**                           finished correctly
** ===================================================================
*/
byte ADCs_Calibrate(bool WaitForResult)
{
  if (ModeFlg != STOP) {               /* Is the device in different mode than "stop"? */
    return ERR_BUSY;                   /* If yes then error */
  }
  ModeFlg = CALIBRATING;               /* Set state of device to the calibration mode */
  (void)AdcLdd1_GetMeasurementCompleteStatus(AdcLdd1_DeviceDataPtr); /* Clear measurement complete status */
  (void)AdcLdd1_StartCalibration(AdcLdd1_DeviceDataPtr); /* Start calibration */
  if (!WaitForResult) {                /* If doesn't wait for result */
    return ERR_OK;                     /* then return ERR_OK, but user have to check the result of calibration e.g. by GetCalibrationStatus method */
  }
  while (!AdcLdd1_GetMeasurementCompleteStatus(AdcLdd1_DeviceDataPtr)) {}; /* Wait until calibration ends */
  if (AdcLdd1_GetCalibrationResultStatus(AdcLdd1_DeviceDataPtr) != ERR_OK) { /* If calibration failed flag is set */
    ModeFlg = STOP;                    /* Set the device to the stop mode */
    return ERR_FAILED;                 /* Return ERR_FAILED error code */
  }
  return ERR_OK;                       /* ADC device is now calibrated */
}

/*
** ===================================================================
**     Method      :  ADCs_OnMeasurementComplete (component ADC)
**
**     Description :
**         The method services the conversion complete interrupt of the
**         selected peripheral(s) and eventually invokes the beans
**         event(s).
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
void AdcLdd1_OnMeasurementComplete(LDD_TUserData *UserDataPtr)
{
  (void)UserDataPtr;                   /* Parameter is not used, suppress unused argument warning */
  if (ModeFlg == CALIBRATING) {        /* If the driver is in CALIBRATING mode */
    (void)AdcLdd1_GetCalibrationResultStatus(AdcLdd1_DeviceDataPtr);
    ModeFlg = STOP;                    /* Set the device to the stop mode */
    ADCs_OnCalibrationEnd();           /* If yes then invoke user event */
    return;                            /* Return from interrupt */
  }
  if (ModeFlg != SINGLE) {
    AdcLdd1_GetMeasuredValues(AdcLdd1_DeviceDataPtr, (LDD_TData *)&ADCs_OutV[SumChan]);
    SumChan++;                         /* Increase counter of measured channels*/
    if (SumChan == 3U) {               /* Is number of measured channels equal to the number of channels used in the component? */
      SumChan = 0U;                    /* If yes then set the counter of measured channels to 0 */
      OutFlg = 0x07U;                  /* Measured values are available */
      ADCs_OnEnd();                    /* If yes then invoke user event */
      ModeFlg = STOP;                  /* Set the device to the stop mode */
      return;                          /* Return from interrupt */
    }
    SampleGroup[0].ChannelIdx = SumChan; /* Start measurement of next channel */
    (void)AdcLdd1_CreateSampleGroup(AdcLdd1_DeviceDataPtr, (LDD_ADC_TSample *)SampleGroup, 1U); /* Configure sample group */
    (void)AdcLdd1_StartSingleMeasurement(AdcLdd1_DeviceDataPtr);
  }
  else {
    AdcLdd1_GetMeasuredValues(AdcLdd1_DeviceDataPtr, (LDD_TData *)&ADCs_OutV[SumChan]);
    OutFlg |= Table[SumChan];          /* Value of measured channel is available */
    ADCs_OnEnd();                      /* If yes then invoke user event */
    ModeFlg = STOP;                    /* Set the device to the stop mode */
  }
}

/*
** ===================================================================
**     Method      :  ADCs_Init (component ADC)
**
**     Description :
**         Initializes the associated peripheral(s) and the component
**         internal variables. The method is called automatically as a
**         part of the application initialization code.
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
void ADCs_Init(void)
{
  OutFlg = 0U;                         /* No measured value */
  ModeFlg = STOP;                      /* Device isn't running */
  AdcLdd1_DeviceDataPtr = AdcLdd1_Init(NULL); /* Calling init method of the inherited component */
}

/* END ADCs. */

#ifdef __cplusplus
}  /* extern "C" */
#endif

/*!
** @}
*/
/*
** ###################################################################
**
**     This file was created by Processor Expert 10.3 [05.09]
**     for the Freescale Kinetis series of microcontrollers.
**
** ###################################################################
*/


text:
import warnings
from collections import namedtuple
from typing import List

import numpy as np
from astropy import units as u
from astropy.coordinates import CartesianRepresentation

from ..ephem import Ephem
from ..frames import Planes
from ..twobody.mean_elements import get_mean_elements
from ..util import norm, time_range
from .util import BODY_COLORS, generate_label

class Trajectory(
    namedtuple("Trajectory", ["coordinates", "position", "label", "colors", "dashed"])
):
    pass

class BaseOrbitPlotter:
    """
    Base class for all the OrbitPlotter classes.
    """

    def __init__(self, num_points=150, *, plane=None):
        self._num_points = num_points
        self._trajectories = []  # type: List[Trajectory]
        self._attractor = None
        self._plane = plane or Planes.EARTH_EQUATOR
        self._attractor_radius = np.inf * u.km

    @property
    def trajectories(self):
        return self._trajectories

    @property
    def plane(self):
        return self._plane

    def _set_attractor(self, attractor):
        if self._attractor is None:
            self._attractor = attractor
        elif attractor is not self._attractor:
            raise NotImplementedError(
                f"Attractor has already been set to {self._attractor.name}"
            )

    def set_attractor(self, attractor):
        """Sets plotting attractor.

        Parameters
        ----------
        attractor : ~poliastro.bodies.Body
            Central body.

        """
        self._set_attractor(attractor)

    def _clear_attractor(self):
        raise NotImplementedError

    def _redraw_attractor(self):
        # Select a sensible value for the radius: realistic for low orbits,
        # visible for high and very high orbits
        min_distance = min(
            [coordinates.norm().min() for coordinates, *_ in self._trajectories]
            or [0 * u.m]
        )
        self._attractor_radius = max(
            self._attractor.R.to(u.km), min_distance.to(u.km) * 0.15
        )

        color = BODY_COLORS.get(self._attractor.name, "#999999")

        self._clear_attractor()

        self._draw_sphere(
            self._attractor_radius, color, self._attractor.name,
        )

    def _redraw(self):
        for trajectory in self._trajectories:
            self.__plot_coordinates_and_position(trajectory)

    def _get_colors(self, color, trail):
        raise NotImplementedError

    def _draw_point(self, radius, color, name, center=None):
        raise NotImplementedError

    def _draw_sphere(self, radius, color, name, center=None):
        raise NotImplementedError

    def _plot_coordinates(self, coordinates, label, colors, dashed):
        raise NotImplementedError

    def _plot_position(self, position, label, colors):
        radius = min(
            self._attractor_radius * 0.5, (norm(position) - self._attractor.R) * 0.5
        )  # Arbitrary thresholds
        self._draw_point(radius, colors[0], label, center=position)

    def __plot_coordinates_and_position(self, trajectory):
        coordinates, position, label, colors, dashed = trajectory

        trace_coordinates = self._plot_coordinates(coordinates, label, colors, dashed)

        if position is not None:
            trace_position = self._plot_position(position, label, colors)
        else:
            trace_position = None

        return trace_coordinates, trace_position

    def __add_trajectory(self, coordinates, position=None, *, label, colors, dashed):
        trajectory = Trajectory(coordinates, position, label, colors, dashed)
        self._trajectories.append(trajectory)

        self._redraw_attractor()

        trace_coordinates, trace_position = self.__plot_coordinates_and_position(
            trajectory
        )

        return trace_coordinates, trace_position

    def _plot_trajectory(self, coordinates, *, label=None, color=None, trail=False):
        if self._attractor is None:
            raise ValueError(
                "An attractor must be set up first, please use "
                "set_attractor(Major_Body) or plot(orbit)"
            )

        colors = self._get_colors(color, trail)

        # Ensure that the coordinates are cartesian just in case,
        # to avoid weird errors later
        coordinates = coordinates.represent_as(CartesianRepresentation)

        return self.__add_trajectory(
            coordinates, None, label=str(label), colors=colors, dashed=False
        )

    def _plot(self, orbit, *, label=None, color=None, trail=False):
        colors = self._get_colors(color, trail)

        self.set_attractor(orbit.attractor)

        orbit = orbit.change_plane(self.plane)

        label = generate_label(orbit.epoch, label)
        coordinates = orbit.sample(self._num_points)

        return self.__add_trajectory(
            coordinates, orbit.r, label=label, colors=colors, dashed=True
        )

    def _plot_body_orbit(
        self, body, epoch, *, label=None, color=None, trail=False,
    ):
        if color is None:
            color = BODY_COLORS.get(body.name)

        self.set_attractor(body.parent)

        # Get approximate, mean value for the period
        period = get_mean_elements(body, epoch).period

        label = generate_label(epoch, label or str(body))
        epochs = time_range(
            epoch, periods=self._num_points, end=epoch + period, scale="tdb"
        )
        ephem = Ephem.from_body(body, epochs, attractor=body.parent, plane=self.plane)

        return self._plot_ephem(ephem, epoch, label=label, color=color, trail=trail)

    def _plot_ephem(self, ephem, epoch=None, *, label=None, color=None, trail=False):
        if self._attractor is None:
            raise ValueError(
                "An attractor must be set up first, please use "
                "set_attractor(Major_Body) or plot(orbit)"
            )

        if ephem.plane is not self.plane:
            raise ValueError(
                f"The ephemerides reference plane is {ephem.plane} "
                f"while the plotter is using {self.plane}, "
                "sample the ephemerides using a different plane "
                "or create a new plotter"
            )

        colors = self._get_colors(color, trail)

        coordinates = ephem.sample()
        if epoch is not None:
            r0 = ephem.rv(epoch)[0]
        else:
            r0 = None

        return self.__add_trajectory(
            coordinates, r0, label=str(label), colors=colors, dashed=False
        )

    def plot_trajectory(self, coordinates, *, label=None, color=None, trail=False):
        """Plots a precomputed trajectory.

        An attractor must be set first.

        Parameters
        ----------
        coordinates : ~astropy.coordinates.CartesianRepresentation
            Trajectory to plot.
        label : string, optional
            Label of the trajectory.
        color : string, optional
            Color of the trajectory.
        trail : bool, optional
            Fade the orbit trail, default to False.

        """
        # Do not return the result of self._plot
        # This behavior might be overriden by subclasses
        self._plot_trajectory(coordinates, label=label, color=color, trail=trail)

    def plot(self, orbit, *, label=None, color=None, trail=False):
        """Plots state and osculating orbit in their plane.

        Parameters
        ----------
        orbit : ~poliastro.twobody.orbit.Orbit
            Orbit to plot.
        label : string, optional
            Label of the orbit.
        color : string, optional
            Color of the line and the position.
        trail : bool, optional
            Fade the orbit trail, default to False.

        """
        # Do not return the result of self._plot
        # This behavior might be overriden by subclasses
        self._plot(orbit, label=label, color=color, trail=trail)

    def plot_body_orbit(
        self, body, epoch, *, label=None, color=None, trail=False,
    ):
        """Plots complete revolution of body and current position.

        Parameters
        ----------
        body : poliastro.bodies.SolarSystemBody
            Body.
        epoch : astropy.time.Time
            Epoch of current position.
        label : str, optional
            Label of the orbit, default to the name of the body.
        color : string, optional
            Color of the line and the position.
        trail : bool, optional
            Fade the orbit trail, default to False.

        """
        # Do not return the result of self._plot
        # This behavior might be overriden by subclasses
        self._plot_body_orbit(body, epoch, label=label, color=color, trail=trail)

    def plot_ephem(self, ephem, epoch=None, *, label=None, color=None, trail=False):
        """Plots Ephem object over its sampling period.

        Parameters
        ----------
        ephem : ~poliastro.ephem.Ephem
            Ephemerides to plot.
        epoch : astropy.time.Time, optional
            Epoch of the current position, none will be used if not given.
        label : str, optional
            Label of the orbit, default to the name of the body.
        color : string, optional
            Color of the line and the position.
        trail : bool, optional
            Fade the orbit trail, default to False.

        """
        # Do not return the result of self._plot
        # This behavior might be overriden by subclasses
        self._plot_ephem(ephem, epoch, label=label, color=color, trail=trail)

class Mixin2D:
    _trajectories: List[Trajectory]

    def _redraw(self):
        raise NotImplementedError

    def _project(self, rr):
        rr_proj = rr - rr.dot(self._frame[2])[:, None] * self._frame[2]
        x = rr_proj.dot(self._frame[0])
        y = rr_proj.dot(self._frame[1])
        return x, y

    def _set_frame(self, p_vec, q_vec, w_vec):
        if not np.allclose([norm(v) for v in (p_vec, q_vec, w_vec)], 1):
            raise ValueError("Vectors must be unit.")
        elif not np.allclose([p_vec.dot(q_vec), q_vec.dot(w_vec), w_vec.dot(p_vec)], 0):
            raise ValueError("Vectors must be mutually orthogonal.")
        else:
            self._frame = p_vec, q_vec, w_vec

        if self._trajectories:
            self._redraw()

    def set_frame(self, p_vec, q_vec, w_vec):
        """Sets perifocal frame.

        Raises
        ------
        ValueError
            If the vectors are not a set of mutually orthogonal unit vectors.

        """
        warnings.warn(
            "Method set_frame is deprecated and will be removed in a future release, "
            "use `set_body_frame` or `set_orbit_frame` instead"
            "with your use case",
            DeprecationWarning,
            stacklevel=2,
        )
        self._set_frame(p_vec, q_vec, w_vec)

    def set_orbit_frame(self, orbit):
        """Sets perifocal frame based on an orbit.

        Parameters
        ----------
        orbit : ~poliastro.twobody.Orbit
            Orbit to use as frame.

        """
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            self._set_frame(*orbit.pqw())

    def set_body_frame(self, body, epoch=None):
        """Sets perifocal frame based on the orbit of a body at a particular epoch if given.

        Parameters
        ----------
        body : poliastro.bodies.SolarSystemBody
            Body.
        epoch : astropy.time.Time, optional
            Epoch of current position.

        """
        from poliastro.twobody import Orbit

        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            orbit = Orbit.from_body_ephem(body, epoch).change_plane(self.plane)  # type: ignore

        self.set_orbit_frame(orbit)

redpajama_set_name: RedPajamaGithub


text:
package org.gradle.util;

import org.apache.commons.lang.StringEscapeUtils;
import org.gradle.internal.SystemProperties;

import java.io.File;
import java.util.regex.Pattern;

public class TextUtil {
    private static final Pattern WHITESPACE = Pattern.compile("\\s*");

    /**
     * Returns the line separator for Windows.
     */
    public static String getWindowsLineSeparator() {
        return "\r\n";
    }

    /**
     * Returns the line separator for Unix.
     */
    public static String getUnixLineSeparator() {
        return "\n";
    }

    /**
     * Returns the line separator for this platform.
     */
    public static String getPlatformLineSeparator() {
        return SystemProperties.getLineSeparator();
    }

    /**
     * Converts all line separators in the specified string to the specified line separator.
     */
    public static String convertLineSeparators(String str, String sep) {
        return str == null ? null : str.replaceAll("\r\n|\r|\n", sep);
    }

    /**
     * Converts all line separators in the specified string to the the platform's line separator.
     */
    public static String toPlatformLineSeparators(String str) {
        return str == null ? null : convertLineSeparators(str, getPlatformLineSeparator());
    }

    /**
     * Converts all line separators in the specified string to a single new line character.
     */
    public static String normaliseLineSeparators(String str) {
        return str == null ? null : convertLineSeparators(str, "\n");
    }

    /**
     * Converts all native file separators in the specified string to '/'.
     */
    public static String normaliseFileSeparators(String path) {
        return path.replaceAll(Pattern.quote(File.separator), "/");
    }

    /**
     * Escapes the toString() representation of {@code obj} for use in a literal string.
     * This is useful for interpolating variables into script strings, as well as in other situations.
     */
    public static String escapeString(Object obj) {
        return obj == null ? null : StringEscapeUtils.escapeJava(obj.toString());
    }

    /**
     * Tells whether the specified string contains any whitespace characters.
     */
    public static boolean containsWhitespace(String str) {
        for (int i = 0; i < str.length(); i++) {
            if (Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * Indents every line of {@code text} by {@code indent}. Empty lines
     * and lines that only contain whitespace are not indented.
     */
    public static String indent(String text, String indent) {
        StringBuilder builder = new StringBuilder();
        String[] lines = text.split("\n");

        for (int i = 0; i < lines.length; i++) {
            String line = lines[i];
            if (!WHITESPACE.matcher(line).matches()) {
                builder.append(indent);
            }
            builder.append(line);
            if (i < lines.length - 1) {
                builder.append('\n');
            }
        }

        return builder.toString();
    }
}


text:
package jsonnet

import (
	"bytes"
	"fmt"
	"io"
	"math"
	"reflect"
	"sort"
	"strconv"

	"github.com/google/go-jsonnet/ast"
	"github.com/google/go-jsonnet/astgen"
)

// TODO(sbarzowski) use it as a pointer in most places b/c it can sometimes be shared
// for example it can be shared between array elements and function arguments
type environment struct {
	selfBinding selfBinding

	// Bindings introduced in this frame. The way previous bindings are treated
	// depends on the type of a frame.
	// If cleanEnv == true then previous bindings are ignored (it's a clean
	// environment with just the variables we have here).
	// If cleanEnv == false then if this frame doesn't contain a binding
	// previous bindings will be used.
	upValues bindingFrame
}

func makeEnvironment(upValues bindingFrame, sb selfBinding) environment {
	return environment{
		upValues:    upValues,
		selfBinding: sb,
	}
}

func (i *interpreter) getCurrentStackTrace() []traceFrame {
	var result []traceFrame
	for _, f := range i.stack.stack {
		if f.cleanEnv {
			result = append(result, traceElementToTraceFrame(f.trace))
		}
	}
	if i.stack.currentTrace.loc != nil {
		result = append(result, traceElementToTraceFrame(i.stack.currentTrace))
	}
	return result
}

type callFrame struct {
	// True if it switches to a clean environment (function call or array element)
	// False otherwise, e.g. for local
	cleanEnv bool

	// Tracing information about the place where it was called from.
	trace traceElement

	// Whether this frame can be removed from the stack when it doesn't affect
	// the evaluation result, but in case of an error, it won't appear on the
	// stack trace.
	// It's used for tail call optimization.
	trimmable bool

	env environment
}

func dumpCallFrame(c *callFrame) string {
	var loc ast.LocationRange
	if c.trace.loc == nil {
		loc = ast.MakeLocationRangeMessage("?")
	} else {
		loc = *c.trace.loc
	}
	return fmt.Sprintf("<callFrame cleanEnv = %t location = %v trimmable = %t>",
		c.cleanEnv,
		loc,
		c.trimmable,
	)
}

type callStack struct {
	calls        int
	limit        int
	stack        []*callFrame
	currentTrace traceElement
}

func dumpCallStack(c *callStack) string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "<callStack calls = %d limit = %d stack:\n", c.calls, c.limit)
	for _, callFrame := range c.stack {
		fmt.Fprintf(&buf, "  %v\n", dumpCallFrame(callFrame))
	}
	buf.WriteString("\n>")
	return buf.String()
}

func (s *callStack) top() *callFrame {
	r := s.stack[len(s.stack)-1]
	return r
}

// It might've been popped already by tail call optimization.
// We check if it was trimmed by comparing the current stack size to the position
// of the frame we want to pop.
func (s *callStack) popIfExists(whichFrame int) {
	if len(s.stack) == whichFrame {
		if s.top().cleanEnv {
			s.calls--
		}
		s.setCurrentTrace(s.stack[len(s.stack)-1].trace)
		s.stack = s.stack[:len(s.stack)-1]
	}
}

/** If there is a trimmable frame followed by some locals, pop them all. */
func (s *callStack) tailCallTrimStack() {
	for i := len(s.stack) - 1; i >= 0; i-- {
		if s.stack[i].cleanEnv {
			if !s.stack[i].trimmable {
				return
			}
			// Remove this stack frame and everything above it
			s.stack = s.stack[:i]
			s.calls--
			return
		}
	}
}

func (s *callStack) setCurrentTrace(trace traceElement) {
	if s.currentTrace != (traceElement{}) {
		panic("Tried to change the traceElement while the old one was still there.")
	}
	s.currentTrace = trace
}

func (s *callStack) clearCurrentTrace() {
	s.currentTrace = traceElement{}
}

type tailCallStatus int

const (
	nonTailCall tailCallStatus = iota
	tailCall
)

func (s *callStack) newCall(env environment, trimmable bool) {
	if s.currentTrace == (traceElement{}) {
		panic("Saving empty traceElement on stack")
	}
	s.stack = append(s.stack, &callFrame{
		cleanEnv:  true,
		trace:     s.currentTrace,
		env:       env,
		trimmable: trimmable,
	})
	s.clearCurrentTrace()
	s.calls++
}

func (s *callStack) newLocal(vars bindingFrame) {
	s.stack = append(s.stack, &callFrame{
		env:   makeEnvironment(vars, selfBinding{}),
		trace: s.currentTrace,
	})
	s.clearCurrentTrace()
}

// getSelfBinding resolves the self construct
func (s *callStack) getSelfBinding() selfBinding {
	for i := len(s.stack) - 1; i >= 0; i-- {
		if s.stack[i].cleanEnv {
			return s.stack[i].env.selfBinding
		}
	}
	panic(fmt.Sprintf("malformed stack %v", dumpCallStack(s)))
}

// lookUpVar finds for the closest variable in scope that matches the given name.
func (s *callStack) lookUpVar(id ast.Identifier) *cachedThunk {
	for i := len(s.stack) - 1; i >= 0; i-- {
		bind, present := s.stack[i].env.upValues[id]
		if present {
			return bind
		}
		if s.stack[i].cleanEnv {
			// Nothing beyond the captured environment of the thunk / closure.
			break
		}
	}
	return nil
}

func (s *callStack) lookUpVarOrPanic(id ast.Identifier) *cachedThunk {
	th := s.lookUpVar(id)
	if th == nil {
		panic(fmt.Sprintf("RUNTIME: Unknown variable: %v (we should have caught this statically)", id))
	}
	return th
}

func (s *callStack) getCurrentEnv(ast ast.Node) environment {
	return makeEnvironment(
		s.capture(ast.FreeVariables()),
		s.getSelfBinding(),
	)
}

// Build a binding frame containing specified variables.
func (s *callStack) capture(freeVars ast.Identifiers) bindingFrame {
	env := make(bindingFrame)
	for _, fv := range freeVars {
		env[fv] = s.lookUpVarOrPanic(fv)
	}
	return env
}

func makeCallStack(limit int) callStack {
	return callStack{
		calls: 0,
		limit: limit,
	}
}

// Keeps current execution context and evaluates things
type interpreter struct {
	// Current stack. It is used for:
	// 1) Keeping environment (object we're in, variables)
	// 2) Diagnostic information in case of failure
	stack callStack

	// External variables
	extVars map[string]*cachedThunk

	// Native functions
	nativeFuncs map[string]*NativeFunction

	// A part of std object common to all files
	baseStd *valueObject

	// Keeps imports
	importCache *importCache

	// Output stream for trace() for
	traceOut io.Writer
}

// Map union, b takes precedence when keys collide.
func addBindings(a, b bindingFrame) bindingFrame {
	result := make(bindingFrame)

	for k, v := range a {
		result[k] = v
	}

	for k, v := range b {
		result[k] = v
	}

	return result
}

func (i *interpreter) newCall(env environment, trimmable bool) error {
	s := &i.stack
	if s.calls >= s.limit {
		return makeRuntimeError("max stack frames exceeded.", i.getCurrentStackTrace())
	}
	s.newCall(env, trimmable)
	return nil
}

func (i *interpreter) evaluate(a ast.Node, tc tailCallStatus) (value, error) {
	trace := traceElement{
		loc:     a.Loc(),
		context: a.Context(),
	}
	oldTrace := i.stack.currentTrace
	i.stack.clearCurrentTrace()
	i.stack.setCurrentTrace(trace)
	defer func() { i.stack.clearCurrentTrace(); i.stack.setCurrentTrace(oldTrace) }()

	switch node := a.(type) {
	case *ast.Array:
		sb := i.stack.getSelfBinding()
		var elements []*cachedThunk
		for _, el := range node.Elements {
			env := makeEnvironment(i.stack.capture(el.Expr.FreeVariables()), sb)
			elThunk := cachedThunk{env: &env, body: el.Expr}
			elements = append(elements, &elThunk)
		}
		return makeValueArray(elements), nil

	case *ast.Binary:
		if node.Op == ast.BopAnd {
			// Special case for shortcut semantics.
			xv, err := i.evaluate(node.Left, nonTailCall)
			if err != nil {
				return nil, err
			}
			x, err := i.getBoolean(xv)
			if err != nil {
				return nil, err
			}
			if !x.value {
				return x, nil
			}
			yv, err := i.evaluate(node.Right, tc)
			if err != nil {
				return nil, err
			}
			return i.getBoolean(yv)
		} else if node.Op == ast.BopOr {
			// Special case for shortcut semantics.
			xv, err := i.evaluate(node.Left, nonTailCall)
			if err != nil {
				return nil, err
			}
			x, err := i.getBoolean(xv)
			if err != nil {
				return nil, err
			}
			if x.value {
				return x, nil
			}
			yv, err := i.evaluate(node.Right, tc)
			if err != nil {
				return nil, err
			}
			return i.getBoolean(yv)

		} else {
			left, err := i.evaluate(node.Left, nonTailCall)
			if err != nil {
				return nil, err
			}
			right, err := i.evaluate(node.Right, tc)
			if err != nil {
				return nil, err
			}
			// TODO(dcunnin): The double dereference here is probably not necessary.
			builtin := bopBuiltins[node.Op]
			return builtin.function(i, left, right)
		}

	case *ast.Unary:
		value, err := i.evaluate(node.Expr, tc)
		if err != nil {
			return nil, err
		}

		builtin := uopBuiltins[node.Op]

		result, err := builtin.function(i, value)
		if err != nil {
			return nil, err
		}
		return result, nil

	case *ast.Conditional:
		cond, err := i.evaluate(node.Cond, nonTailCall)
		if err != nil {
			return nil, err
		}
		condBool, err := i.getBoolean(cond)
		if err != nil {
			return nil, err
		}
		if condBool.value {
			return i.evaluate(node.BranchTrue, tc)
		}
		return i.evaluate(node.BranchFalse, tc)

	case *ast.DesugaredObject:
		// Evaluate all the field names.  Check for null, dups, etc.
		fields := make(simpleObjectFieldMap)
		for _, field := range node.Fields {
			fieldNameValue, err := i.evaluate(field.Name, nonTailCall)
			if err != nil {
				return nil, err
			}
			var fieldName string
			switch fieldNameValue := fieldNameValue.(type) {
			case valueString:
				fieldName = fieldNameValue.getGoString()
			case *valueNull:
				// Omitted field.
				continue
			default:
				return nil, i.Error(fmt.Sprintf("Field name must be string, got %v", fieldNameValue.getType().name))
			}

			if _, ok := fields[fieldName]; ok {
				return nil, i.Error(duplicateFieldNameErrMsg(fieldName))
			}
			var f unboundField = &codeUnboundField{field.Body}
			if field.PlusSuper {
				f = &plusSuperUnboundField{f}
			}
			fields[fieldName] = simpleObjectField{field.Hide, f}
		}
		var asserts []unboundField
		for _, assert := range node.Asserts {
			asserts = append(asserts, &codeUnboundField{assert})
		}
		var locals []objectLocal
		for _, local := range node.Locals {
			locals = append(locals, objectLocal{name: local.Variable, node: local.Body})
		}
		upValues := i.stack.capture(node.FreeVariables())
		return makeValueSimpleObject(upValues, fields, asserts, locals), nil

	case *ast.Error:
		msgVal, err := i.evaluate(node.Expr, nonTailCall)
		if err != nil {
			// error when evaluating error message
			return nil, err
		}
		if msgVal.getType() != stringType {
			msgVal, err = builtinToString(i, msgVal)
			if err != nil {
				return nil, err
			}
		}
		msg, err := i.getString(msgVal)
		if err != nil {
			return nil, err
		}
		return nil, i.Error(msg.getGoString())

	case *ast.Index:
		targetValue, err := i.evaluate(node.Target, nonTailCall)
		if err != nil {
			return nil, err
		}
		index, err := i.evaluate(node.Index, nonTailCall)
		if err != nil {
			return nil, err
		}
		switch target := targetValue.(type) {
		case *valueObject:
			indexString, err := i.getString(index)
			if err != nil {
				return nil, err
			}
			return target.index(i, indexString.getGoString())
		case *valueArray:
			indexInt, err := i.getNumber(index)
			if err != nil {
				return nil, err
			}
			// TODO(https://github.com/google/jsonnet/issues/377): non-integer indexes should be an error
			return target.index(i, int(indexInt.value))

		case valueString:
			indexInt, err := i.getNumber(index)
			if err != nil {
				return nil, err
			}
			// TODO(https://github.com/google/jsonnet/issues/377): non-integer indexes should be an error
			return target.index(i, int(indexInt.value))
		}

		return nil, i.Error(fmt.Sprintf("Value non indexable: %v", reflect.TypeOf(targetValue)))

	case *ast.Import:
		codePath := node.Loc().FileName
		return i.importCache.importCode(codePath, node.File.Value, i)

	case *ast.ImportStr:
		codePath := node.Loc().FileName
		return i.importCache.importString(codePath, node.File.Value, i)

	case *ast.LiteralBoolean:
		return makeValueBoolean(node.Value), nil

	case *ast.LiteralNull:
		return makeValueNull(), nil

	case *ast.LiteralNumber:
		// Since the lexer ensures that OriginalString is of
		// the right form, this will only fail if the number is
		// too large to fit in a double.
		num, err := strconv.ParseFloat(node.OriginalString, 64)
		if err != nil {
			return nil, i.Error("overflow")
		}
		return makeValueNumber(num), nil

	case *ast.LiteralString:
		return makeValueString(node.Value), nil

	case *ast.Local:
		vars := make(bindingFrame)
		bindEnv := i.stack.getCurrentEnv(a)
		for _, bind := range node.Binds {
			th := cachedThunk{env: &bindEnv, body: bind.Body}

			// recursive locals
			vars[bind.Variable] = &th
			bindEnv.upValues[bind.Variable] = &th
		}
		i.stack.newLocal(vars)
		sz := len(i.stack.stack)
		// Add new stack frame, with new thunk for this variable
		// execute body WRT stack frame.
		v, err := i.evaluate(node.Body, tc)
		i.stack.popIfExists(sz)

		return v, err

	case *ast.Self:
		sb := i.stack.getSelfBinding()
		return sb.self, nil

	case *ast.Var:
		foo := i.stack.lookUpVarOrPanic(node.Id)
		return foo.getValue(i)

	case *ast.SuperIndex:
		index, err := i.evaluate(node.Index, nonTailCall)
		if err != nil {
			return nil, err
		}
		indexStr, err := i.getString(index)
		if err != nil {
			return nil, err
		}
		return objectIndex(i, i.stack.getSelfBinding().super(), indexStr.getGoString())

	case *ast.InSuper:
		index, err := i.evaluate(node.Index, nonTailCall)
		if err != nil {
			return nil, err
		}
		indexStr, err := i.getString(index)
		if err != nil {
			return nil, err
		}
		hasField := objectHasField(i.stack.getSelfBinding().super(), indexStr.getGoString(), withHidden)
		return makeValueBoolean(hasField), nil

	case *ast.Function:
		return &valueFunction{
			ec: makeClosure(i.stack.getCurrentEnv(a), node),
		}, nil

	case *ast.Apply:
		// Eval target
		target, err := i.evaluate(node.Target, nonTailCall)
		if err != nil {
			return nil, err
		}
		function, err := i.getFunction(target)
		if err != nil {
			return nil, err
		}

		// environment in which we can evaluate arguments
		argEnv := i.stack.getCurrentEnv(a)
		arguments := callArguments{
			positional: make([]*cachedThunk, len(node.Arguments.Positional)),
			named:      make([]namedCallArgument, len(node.Arguments.Named)),
			tailstrict: node.TailStrict,
		}
		for i, arg := range node.Arguments.Positional {
			arguments.positional[i] = &cachedThunk{env: &argEnv, body: arg.Expr}
		}

		for i, arg := range node.Arguments.Named {
			arguments.named[i] = namedCallArgument{name: arg.Name, pv: &cachedThunk{env: &argEnv, body: arg.Arg}}
		}
		return i.evaluateTailCall(function, arguments, tc)

	case *astMakeArrayElement:
		arguments := callArguments{
			positional: []*cachedThunk{
				&cachedThunk{
					content: intToValue(node.index),
				},
			},
		}
		return i.evaluateTailCall(node.function, arguments, tc)

	default:
		panic(fmt.Sprintf("Executing this AST type not implemented: %v", reflect.TypeOf(a)))
	}
}

// unparseString Wraps in "" and escapes stuff to make the string JSON-compliant and human-readable.
func unparseString(v string) string {
	var buf bytes.Buffer
	buf.WriteString("\"")
	for _, c := range v {
		switch c {
		case '"':
			buf.WriteString("\\\"")
		case '\\':
			buf.WriteString("\\\\")
		case '\b':
			buf.WriteString("\\b")
		case '\f':
			buf.WriteString("\\f")
		case '\n':
			buf.WriteString("\\n")
		case '\r':
			buf.WriteString("\\r")
		case '\t':
			buf.WriteString("\\t")
		case 0:
			buf.WriteString("\\u0000")
		default:
			if c < 0x20 || (c >= 0x7f && c <= 0x9f) {
				buf.WriteString(fmt.Sprintf("\\u%04x", int(c)))
			} else {
				buf.WriteRune(c)
			}
		}
	}
	buf.WriteString("\"")
	return buf.String()
}

func unparseNumber(v float64) string {
	if v == math.Floor(v) {
		return fmt.Sprintf("%.0f", v)
	}

	// See "What Every Computer Scientist Should Know About Floating-Point Arithmetic"
	// Theorem 15
	// http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
	return fmt.Sprintf("%.17g", v)
}

// manifestJSON converts to standard JSON representation as in "encoding/json" package
func (i *interpreter) manifestJSON(v value) (interface{}, error) {
	// TODO(sbarzowski) Add nice stack traces indicating the part of the code which
	// evaluates to non-manifestable value (that might require passing context about
	// the root value)
	if i.stack.currentTrace == (traceElement{}) {
		panic("manifesting JSON with empty traceElement")
	}

	// Fresh frame for better stack traces
	err := i.newCall(environment{}, false)
	if err != nil {
		return nil, err
	}
	stackSize := len(i.stack.stack)
	defer i.stack.popIfExists(stackSize)

	switch v := v.(type) {

	case *valueBoolean:
		return v.value, nil

	case *valueFunction:
		return nil, makeRuntimeError("couldn't manifest function as JSON", i.getCurrentStackTrace())

	case *valueNumber:
		return v.value, nil

	case valueString:
		return v.getGoString(), nil

	case *valueNull:
		return nil, nil

	case *valueArray:
		result := make([]interface{}, 0, len(v.elements))
		for index, th := range v.elements {
			msg := ast.MakeLocationRangeMessage(fmt.Sprintf("Array element %d", index))
			i.stack.setCurrentTrace(traceElement{
				loc: &msg,
			})
			elVal, err := i.evaluatePV(th)
			if err != nil {
				i.stack.clearCurrentTrace()
				return nil, err
			}
			elem, err := i.manifestJSON(elVal)
			if err != nil {
				i.stack.clearCurrentTrace()
				return nil, err
			}
			result = append(result, elem)
			i.stack.clearCurrentTrace()
		}
		return result, nil

	case *valueObject:
		fieldNames := objectFields(v, withoutHidden)
		sort.Strings(fieldNames)

		msg := ast.MakeLocationRangeMessage("Checking object assertions")
		i.stack.setCurrentTrace(traceElement{
			loc: &msg,
		})
		err := checkAssertions(i, v)
		if err != nil {
			i.stack.clearCurrentTrace()
			return nil, err
		}
		i.stack.clearCurrentTrace()

		result := make(map[string]interface{})

		for _, fieldName := range fieldNames {
			msg := ast.MakeLocationRangeMessage(fmt.Sprintf("Field %#v", fieldName))
			i.stack.setCurrentTrace(traceElement{
				loc: &msg,
			})
			fieldVal, err := v.index(i, fieldName)
			if err != nil {
				i.stack.clearCurrentTrace()
				return nil, err
			}

			field, err := i.manifestJSON(fieldVal)
			if err != nil {
				i.stack.clearCurrentTrace()
				return nil, err
			}
			result[fieldName] = field
			i.stack.clearCurrentTrace()
		}

		return result, nil

	default:
		return nil, makeRuntimeError(
			fmt.Sprintf("manifesting this value not implemented yet: %s", reflect.TypeOf(v)),
			i.getCurrentStackTrace(),
		)

	}
}

func serializeJSON(v interface{}, multiline bool, indent string, buf *bytes.Buffer) {
	switch v := v.(type) {
	case nil:
		buf.WriteString("null")

	case []interface{}:
		if len(v) == 0 {
			buf.WriteString("[ ]")
		} else {
			var prefix string
			var indent2 string
			if multiline {
				prefix = "[\n"
				indent2 = indent + "   "
			} else {
				prefix = "["
				indent2 = indent
			}
			for _, elem := range v {
				buf.WriteString(prefix)
				buf.WriteString(indent2)
				serializeJSON(elem, multiline, indent2, buf)
				if multiline {
					prefix = ",\n"
				} else {
					prefix = ", "
				}
			}
			if multiline {
				buf.WriteString("\n")
			}
			buf.WriteString(indent)
			buf.WriteString("]")
		}

	case bool:
		if v {
			buf.WriteString("true")
		} else {
			buf.WriteString("false")
		}

	case float64:
		buf.WriteString(unparseNumber(v))

	case map[string]interface{}:
		fieldNames := make([]string, 0, len(v))
		for name := range v {
			fieldNames = append(fieldNames, name)
		}
		sort.Strings(fieldNames)

		if len(fieldNames) == 0 {
			buf.WriteString("{ }")
		} else {
			var prefix string
			var indent2 string
			if multiline {
				prefix = "{\n"
				indent2 = indent + "   "
			} else {
				prefix = "{"
				indent2 = indent
			}
			for _, fieldName := range fieldNames {
				fieldVal := v[fieldName]

				buf.WriteString(prefix)
				buf.WriteString(indent2)

				buf.WriteString(unparseString(fieldName))
				buf.WriteString(": ")

				serializeJSON(fieldVal, multiline, indent2, buf)

				if multiline {
					prefix = ",\n"
				} else {
					prefix = ", "
				}
			}

			if multiline {
				buf.WriteString("\n")
			}
			buf.WriteString(indent)
			buf.WriteString("}")
		}

	case string:
		buf.WriteString(unparseString(v))

	default:
		panic(fmt.Sprintf("Unsupported value for serialization %#+v", v))
	}
}

func (i *interpreter) manifestAndSerializeJSON(
	buf *bytes.Buffer, v value, multiline bool, indent string) error {
	manifested, err := i.manifestJSON(v)
	if err != nil {
		return err
	}
	serializeJSON(manifested, multiline, indent, buf)
	return nil
}

// manifestString expects the value to be a string and returns it.
func (i *interpreter) manifestString(buf *bytes.Buffer, v value) error {
	switch v := v.(type) {
	case valueString:
		buf.WriteString(v.getGoString())
		return nil
	default:
		return makeRuntimeError(fmt.Sprintf("expected string result, got: %s", v.getType().name), i.getCurrentStackTrace())
	}
}

func (i *interpreter) manifestAndSerializeMulti(v value, stringOutputMode bool) (r map[string]string, err error) {
	r = make(map[string]string)
	json, err := i.manifestJSON(v)
	if err != nil {
		return r, err
	}
	switch json := json.(type) {
	case map[string]interface{}:
		for filename, fileJSON := range json {
			if stringOutputMode {
				switch val := fileJSON.(type) {
				case string:
					r[filename] = val
				default:
					msg := fmt.Sprintf("multi mode: top-level object's key %s has a value of type %T, "+
						"should be a string", filename, val)
					return r, makeRuntimeError(msg, i.getCurrentStackTrace())
				}
			} else {
				var buf bytes.Buffer
				serializeJSON(fileJSON, true, "", &buf)
				buf.WriteString("\n")
				r[filename] = buf.String()
			}
		}
	default:
		msg := fmt.Sprintf("multi mode: top-level object was a %s, "+
			"should be an object whose keys are filenames and values hold "+
			"the JSON for that file.", v.getType().name)
		return r, makeRuntimeError(msg, i.getCurrentStackTrace())
	}
	return
}

func (i *interpreter) manifestAndSerializeYAMLStream(v value) (r []string, err error) {
	r = make([]string, 0)
	json, err := i.manifestJSON(v)
	if err != nil {
		return r, err
	}
	switch json := json.(type) {
	case []interface{}:
		for _, doc := range json {
			var buf bytes.Buffer
			serializeJSON(doc, true, "", &buf)
			buf.WriteString("\n")
			r = append(r, buf.String())
		}
	default:
		msg := fmt.Sprintf("stream mode: top-level object was a %s, "+
			"should be an array whose elements hold "+
			"the JSON for each document in the stream.", v.getType().name)
		return r, makeRuntimeError(msg, i.getCurrentStackTrace())
	}
	return
}

func jsonToValue(i *interpreter, v interface{}) (value, error) {
	switch v := v.(type) {
	case nil:
		return &nullValue, nil

	case []interface{}:
		elems := make([]*cachedThunk, len(v))
		for counter, elem := range v {
			val, err := jsonToValue(i, elem)
			if err != nil {
				return nil, err
			}
			elems[counter] = readyThunk(val)
		}
		return makeValueArray(elems), nil

	case bool:
		return makeValueBoolean(v), nil
	case float64:
		return makeDoubleCheck(i, v)

	case map[string]interface{}:
		fieldMap := map[string]value{}
		for name, f := range v {
			val, err := jsonToValue(i, f)
			if err != nil {
				return nil, err
			}
			fieldMap[name] = val
		}
		return buildObject(ast.ObjectFieldInherit, fieldMap), nil

	case string:
		return makeValueString(v), nil

	default:
		return nil, i.Error(fmt.Sprintf("Not a json type: %#+v", v))
	}
}

func (i *interpreter) EvalInCleanEnv(env *environment, ast ast.Node, trimmable bool) (value, error) {
	err := i.newCall(*env, trimmable)
	if err != nil {
		return nil, err
	}
	stackSize := len(i.stack.stack)

	val, err := i.evaluate(ast, tailCall)
	if err != nil {
		return nil, err
	}

	i.stack.popIfExists(stackSize)

	return val, nil
}

func (i *interpreter) evaluatePV(ph potentialValue) (value, error) {
	return ph.getValue(i)
}

func (i *interpreter) evaluateTailCall(function *valueFunction, args callArguments, tc tailCallStatus) (value, error) {
	if tc == tailCall {
		i.stack.tailCallTrimStack()
	}
	return function.call(i, args)
}

func (i *interpreter) Error(s string) error {
	err := makeRuntimeError(s, i.getCurrentStackTrace())
	return err
}

func (i *interpreter) typeErrorSpecific(bad value, good value) error {
	return i.Error(
		fmt.Sprintf("Unexpected type %v, expected %v", bad.getType().name, good.getType().name),
	)
}

func (i *interpreter) typeErrorGeneral(bad value) error {
	return i.Error(
		fmt.Sprintf("Unexpected type %v", bad.getType().name),
	)
}

func (i *interpreter) getNumber(val value) (*valueNumber, error) {
	switch v := val.(type) {
	case *valueNumber:
		return v, nil
	default:
		return nil, i.typeErrorSpecific(val, &valueNumber{})
	}
}

//nolint:unused
func (i *interpreter) evaluateNumber(pv potentialValue) (*valueNumber, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return nil, err
	}
	return i.getNumber(v)
}

func (i *interpreter) getInt(val value) (int, error) {
	num, err := i.getNumber(val)
	if err != nil {
		return 0, err
	}
	// We conservatively convert ot int32, so that it can be machine-sized int
	// on any machine. And it's used only for indexing anyway.
	intNum := int(int32(num.value))
	if float64(intNum) != num.value {
		return 0, i.Error(fmt.Sprintf("Expected an integer, but got %v", num.value))
	}
	return intNum, nil
}

func (i *interpreter) evaluateInt(pv potentialValue) (int, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return 0, err
	}
	return i.getInt(v)
}

//nolint:unused
func (i *interpreter) getInt64(val value) (int64, error) {
	num, err := i.getNumber(val)
	if err != nil {
		return 0, err
	}
	intNum := int64(num.value)
	if float64(intNum) != num.value {
		return 0, i.Error(fmt.Sprintf("Expected an integer, but got %v", num.value))
	}
	return intNum, nil
}

//nolint:unused
func (i *interpreter) evaluateInt64(pv potentialValue) (int64, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return 0, err
	}
	return i.getInt64(v)
}

func (i *interpreter) getString(val value) (valueString, error) {
	switch v := val.(type) {
	case valueString:
		return v, nil
	default:
		return nil, i.typeErrorSpecific(val, emptyString())
	}
}

//nolint:unused
func (i *interpreter) evaluateString(pv potentialValue) (valueString, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return nil, err
	}
	return i.getString(v)
}

func (i *interpreter) getBoolean(val value) (*valueBoolean, error) {
	switch v := val.(type) {
	case *valueBoolean:
		return v, nil
	default:
		return nil, i.typeErrorSpecific(val, &valueBoolean{})
	}
}

//nolint:unused
func (i *interpreter) evaluateBoolean(pv potentialValue) (*valueBoolean, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return nil, err
	}
	return i.getBoolean(v)
}

func (i *interpreter) getArray(val value) (*valueArray, error) {
	switch v := val.(type) {
	case *valueArray:
		return v, nil
	default:
		return nil, i.typeErrorSpecific(val, &valueArray{})
	}
}

//nolint:unused
func (i *interpreter) evaluateArray(pv potentialValue) (*valueArray, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return nil, err
	}
	return i.getArray(v)
}

func (i *interpreter) getFunction(val value) (*valueFunction, error) {
	switch v := val.(type) {
	case *valueFunction:
		return v, nil
	default:
		return nil, i.typeErrorSpecific(val, &valueFunction{})
	}
}

//nolint:unused
func (i *interpreter) evaluateFunction(pv potentialValue) (*valueFunction, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return nil, err
	}
	return i.getFunction(v)
}

func (i *interpreter) getObject(val value) (*valueObject, error) {
	switch v := val.(type) {
	case *valueObject:
		return v, nil
	default:
		return nil, i.typeErrorSpecific(val, &valueObject{})
	}
}

func (i *interpreter) evaluateObject(pv potentialValue) (*valueObject, error) {
	v, err := i.evaluatePV(pv)
	if err != nil {
		return nil, err
	}
	return i.getObject(v)
}

func buildStdObject(i *interpreter) (*valueObject, error) {
	objVal, err := evaluateStd(i)
	if err != nil {
		return nil, err
	}
	obj := objVal.(*valueObject).uncached.(*simpleObject)
	builtinFields := map[string]unboundField{}
	for key, ec := range funcBuiltins {
		function := valueFunction{ec: ec} // TODO(sbarzowski) better way to build function value
		builtinFields[key] = &readyValue{&function}
	}

	for name, value := range builtinFields {
		obj.fields[name] = simpleObjectField{ast.ObjectFieldHidden, value}
	}
	return objVal.(*valueObject), nil
}

func evaluateStd(i *interpreter) (value, error) {
	beforeStdEnv := makeEnvironment(
		bindingFrame{},
		makeUnboundSelfBinding(),
	)
	evalLoc := ast.MakeLocationRangeMessage("During evaluation of std")
	evalTrace := traceElement{loc: &evalLoc}
	node := astgen.StdAst
	i.stack.setCurrentTrace(evalTrace)
	defer i.stack.clearCurrentTrace()
	return i.EvalInCleanEnv(&beforeStdEnv, node, false)
}

func prepareExtVars(i *interpreter, ext vmExtMap, kind string) map[string]*cachedThunk {
	result := make(map[string]*cachedThunk)
	for name, content := range ext {
		diagnosticFile := "<" + kind + ":" + name + ">"
		switch content.kind {
		case extKindCode:
			result[name] = codeToPV(i, diagnosticFile, content.value)
		case extKindNode:
			result[name] = nodeToPV(i, diagnosticFile, content.node)
		default:
			result[name] = readyThunk(makeValueString(content.value))
		}
	}
	return result
}

func buildObject(hide ast.ObjectFieldHide, fields map[string]value) *valueObject {
	fieldMap := simpleObjectFieldMap{}
	for name, v := range fields {
		fieldMap[name] = simpleObjectField{hide, &readyValue{v}}
	}
	return makeValueSimpleObject(bindingFrame{}, fieldMap, nil, nil)
}

func buildInterpreter(ext vmExtMap, nativeFuncs map[string]*NativeFunction, maxStack int, ic *importCache, traceOut io.Writer) (*interpreter, error) {
	i := interpreter{
		stack:       makeCallStack(maxStack),
		importCache: ic,
		traceOut:    traceOut,
		nativeFuncs: nativeFuncs,
	}

	stdObj, err := buildStdObject(&i)
	if err != nil {
		return nil, err
	}

	i.baseStd = stdObj

	i.extVars = prepareExtVars(&i, ext, "extvar")

	return &i, nil
}

func makeInitialEnv(filename string, baseStd *valueObject) environment {
	fileSpecific := buildObject(ast.ObjectFieldHidden, map[string]value{
		"thisFile": makeValueString(filename),
	})
	return makeEnvironment(
		bindingFrame{
			"std": readyThunk(makeValueExtendedObject(baseStd, fileSpecific)),
		},
		makeUnboundSelfBinding(),
	)
}

func manifestationTrace() traceElement {
	manifestationLoc := ast.MakeLocationRangeMessage("During manifestation")
	return traceElement{
		loc: &manifestationLoc,
	}
}

func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, error) {
	evalLoc := ast.MakeLocationRangeMessage("During evaluation")
	evalTrace := traceElement{
		loc: &evalLoc,
	}
	env := makeInitialEnv(node.Loc().FileName, i.baseStd)
	i.stack.setCurrentTrace(evalTrace)
	result, err := i.EvalInCleanEnv(&env, node, false)
	i.stack.clearCurrentTrace()
	if err != nil {
		return nil, err
	}
	// If it's not a function, ignore TLA
	if f, ok := result.(*valueFunction); ok {
		toplevelArgMap := prepareExtVars(i, tla, "top-level-arg")
		args := callArguments{}
		for argName, pv := range toplevelArgMap {
			args.named = append(args.named, namedCallArgument{name: ast.Identifier(argName), pv: pv})
		}
		funcLoc := ast.MakeLocationRangeMessage("Top-level function call")
		funcTrace := traceElement{
			loc: &funcLoc,
		}
		i.stack.setCurrentTrace(funcTrace)
		result, err = f.call(i, args)
		i.stack.clearCurrentTrace()
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead
func evaluate(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]*NativeFunction,
	maxStack int, ic *importCache, traceOut io.Writer, stringOutputMode bool) (string, error) {

	i, err := buildInterpreter(ext, nativeFuncs, maxStack, ic, traceOut)
	if err != nil {
		return "", err
	}

	result, err := evaluateAux(i, node, tla)
	if err != nil {
		return "", err
	}

	var buf bytes.Buffer
	i.stack.setCurrentTrace(manifestationTrace())
	if stringOutputMode {
		err = i.manifestString(&buf, result)
	} else {
		err = i.manifestAndSerializeJSON(&buf, result, true, "")
	}
	i.stack.clearCurrentTrace()
	if err != nil {
		return "", err
	}
	buf.WriteString("\n")
	return buf.String(), nil
}

// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead
func evaluateMulti(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]*NativeFunction,
	maxStack int, ic *importCache, traceOut io.Writer, stringOutputMode bool) (map[string]string, error) {

	i, err := buildInterpreter(ext, nativeFuncs, maxStack, ic, traceOut)
	if err != nil {
		return nil, err
	}

	result, err := evaluateAux(i, node, tla)
	if err != nil {
		return nil, err
	}

	i.stack.setCurrentTrace(manifestationTrace())
	manifested, err := i.manifestAndSerializeMulti(result, stringOutputMode)
	i.stack.clearCurrentTrace()
	return manifested, err
}

// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead
func evaluateStream(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]*NativeFunction,
	maxStack int, ic *importCache, traceOut io.Writer) ([]string, error) {

	i, err := buildInterpreter(ext, nativeFuncs, maxStack, ic, traceOut)
	if err != nil {
		return nil, err
	}

	result, err := evaluateAux(i, node, tla)
	if err != nil {
		return nil, err
	}

	i.stack.setCurrentTrace(manifestationTrace())
	manifested, err := i.manifestAndSerializeYAMLStream(result)
	i.stack.clearCurrentTrace()
	return manifested, err
}


text:
/**
 * @file
 **/

#pragma once

#include <memory>

#include "modules/planning/scenarios/dead_end/deadend_turnaround/deadend_turnaround_scenario.h"
#include "modules/planning/scenarios/stage.h"

namespace apollo {
namespace planning {
namespace scenario {
namespace deadend_turnaround {

class StageTurning : public Stage {
 public:
  StageTurning(const ScenarioConfig::StageConfig& config,
               const std::shared_ptr<DependencyInjector>& injector)
      : Stage(config, injector) {}

 private:
  Stage::StageStatus Process(const common::TrajectoryPoint& planning_init_point,
                             Frame* frame) override;

  DeadEndTurnAroundContext* GetContext() {
    return GetContextAs<DeadEndTurnAroundContext>();
  }

 private:
  Stage::StageStatus FinishStage();

 private:
  ScenarioDeadEndTurnAroundConfig scenario_config_;
};

}  // namespace deadend_turnaround
}  // namespace scenario
}  // namespace planning
}  // namespace apollo


text:
import _ from 'lodash';
import https from 'https';
import bigRat from 'big-rational';

import utils from '../../js/utils';

let constants = require("../../js/constants");
let fixtures = require("../../js/fixtures");

let TicketActions = function() {

  this.loadTicketIDs = function(init) {
    if (this.flux.stores.config.debug)
      console.count("loadTicketIDs triggered");

    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.getTicketIDs(function(ticketIDs) {
      this.dispatch(constants.ticket.LOAD_TICKET_IDS, ticketIDs);
      if (init)
        this.flux.actions.ticket.loadTickets(ticketIDs);
    }.bind(this), function(error) {
      this.dispatch(constants.ticket.LOAD_TICKET_IDS_FAIL, {error: error});
    }.bind(this));
  };

  this.loadTickets = function(ticketIDs) {
    if (this.flux.stores.config.debug)
      console.count("loadTickets triggered");

    // Put tickets in loading state
    this.dispatch(constants.ticket.LOAD_TICKETS);

    for (var i = ticketIDs.length - 1; i >= 0; i--)
      this.flux.actions.ticket.loadTicket(ticketIDs[i]);
  };

  this.loadTicket = function(id) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    var tickets = this.flux.store("TicketStore").getState();
    if (tickets.error)
      this.flux.actions.ticket.ticketsLoaded();

    btcSwapClient.lookupTicket(id, function(ticket) {
      if (ticket && ticket.id)
        ticket = this.flux.actions.ticket.setTicketFlags(ticket);

      this.dispatch(constants.ticket.LOAD_TICKET, ticket);

      this.flux.actions.ticket.updateProgress();
    }.bind(this), function(error) {
      this.dispatch(constants.ticket.LOAD_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.updateTicket = function(id) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.lookupTicket(id, function(ticket) {
      if (ticket.id)
        this.dispatch(constants.ticket.UPDATE_TICKET, ticket);
    }.bind(this), function(error) {
      this.dispatch(constants.ticket.LOAD_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.updateProgress = function() {
    var tickets = this.flux.store("TicketStore").getState();
    var current = tickets.progress + 1;
    var total = tickets.ticketIDs.length;

    if (!total) {
      this.dispatch(constants.ticket.LOAD_TICKETS_PROGRESS, {
        progress: current,
        percent: current
      });
      return;
    }

    var percent = parseFloat(((current / total) * 100).toFixed(2));

    this.dispatch(constants.ticket.LOAD_TICKETS_PROGRESS, {
      progress: current,
      percent: percent
    });

    if (percent >= 100)
        this.flux.actions.ticket.ticketsLoaded();
  };

  this.ticketsLoaded = function() {
    if (this.flux.stores.config.debug)
      console.count("ticketsLoaded triggered");

    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    // Watch tickets
    btcSwapClient.watchTickets(function(ticketEvent, ticketId) {
      if (ticketEvent == 'new' || ticketEvent == 'reserved')
        this.flux.actions.ticket.updateTicket(ticketId);
      else if (ticketEvent == 'removed')
        this.dispatch(constants.ticket.CANCEL_TICKET_SUCCESS, ticketId);
    }.bind(this), function(error) {
      this.dispatch(constants.ticket.LOAD_TICKET_FAIL, {error: error});
    }.bind(this));

    // Get BTC block height and hash
    this.flux.actions.ticket.getBlockchainHead();
    this.flux.actions.ticket.getLastBlockHeight();

    this.dispatch(constants.ticket.LOAD_TICKETS_SUCCESS);
  };

  this.setTicketFlags = function(ticket) {
    var userState = this.flux.stores.UserStore.getState();

    var reservable = false;
    if (!ticket.claimer ||
        (ticket.expiry > 1 && ticket.expiry < new Date().getTime() / 1000)) {
      reservable = true;
      ticket.txHash = null;
      ticket.claimer = null;
      ticket.expiry = 1;
    }

    var claimable = false;
    if (!reservable && ticket.claimer == userState.user.id.substr(2))
      claimable = true;

    ticket.formattedAmount = utils.formatEther(ticket.amount);
    ticket.reservable = reservable;
    ticket.claimable = claimable;
    ticket.blockFee = null;
    ticket.formattedBlockFee = {value: null, unit: null};

    return ticket;
  };

  this.lookupTicket = function(id) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.lookupTicket(id, function(ticket) {
      if (this.flux.stores.config.debug)
        utils.log('Ticket: ', ticket);

      if (!ticket || !ticket.id) {
        ticket = {
          id: null,
          address: null,
          amount: null,
          formattedAmount: {value: null, unit: null},
          price: null,
          total: null,
          blockFee: null,
          formattedBlockFee: {value: null, unit: null},
          reservable: false,
          expiry: 1
        };
        this.dispatch(constants.ticket.LOOKUP_TICKET, ticket);
      }
      else {
        ticket = this.flux.actions.ticket.setTicketFlags(ticket);

        this.dispatch(constants.ticket.LOOKUP_TICKET, ticket);

        // TODO live / testnet handling
        if (ticket.txHash && ticket.claimable)
          this.flux.actions.ticket.lookupBitcoinTxHash(ticket, false);
      }
    }.bind(this), function(error) {
      this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.lookupBitcoinTxHash = function(ticket, live) {
    var options = {
      hostname: (live ? '' : 't') + 'btc.blockr.io',
      port: 443,
      path: '/api/v1/tx/raw/' + ticket.txHash,
      method: 'GET',
      withCredentials: false
    };

    var error;
    var req = https.request(options, function(res) {
      if (!res || res.statusCode !== 200) {
        error = "Error retrieving BTC transaction.";
        this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
        utils.error(error);
        return;
      }

      res.on('data', function(data) {
        var json = JSON.parse(data);

        if (json.status !== 'success') {
          error = "Error retrieving BTC transaction data.";
          this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
          utils.error(error, json);
          return;
        }

        data = json.data;
        // TODO check scriptpubkeys, etc exist
        if (!data || !data.tx || !data.tx.vout || data.tx.vout.length < 2 || !data.tx.hex || !data.tx.blockhash) {
          error = "Not enough data in BTC transaction, please wait until it gets mined.";
          this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
          utils.error(error);
          return;
        }

        ticket.rawTx = data.tx.hex;
        ticket.btcPayment = data.tx.vout[0].value;
        ticket.paymentAddr = data.tx.vout[0].scriptPubKey.addresses[0];

        var tx1Script = data.tx.vout[1].scriptPubKey.hex;
        var etherAddr;
        if (tx1Script && tx1Script.length === 50 &&
            tx1Script.slice(0, 6) === '76a914' && tx1Script.slice(-4) === '88ac') { // WTF is this
          etherAddr = tx1Script.slice(6, -4);
        }
        else {
          etherAddr = 'INVALID';
          error = 'Invalid Ethereum address:';
          this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error + " " + tx1Script});
          utils.error(error, tx1Script);
        }
        ticket.etherAddr = etherAddr;

        var feePercentage = bigRat("10").pow("8").times(bigRat(data.tx.vout[1].value)).mod(10000);
        ticket.feePercentage = bigRat(feePercentage).divide(100).valueOf(10) + '%';

        ticket.feeAmount = bigRat(feePercentage).times(ticket.amount).divide(10000).toDecimal();
        ticket.formattedFee = utils.formatEther(ticket.feeAmount);

        if (data.tx.blockhash)
          this.flux.actions.ticket.lookupExtendedDetails(ticket, data.tx.blockhash, live);
        else {
          error = "Missing block hash on transaction.";
          this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
          utils.error(error);
        }

      }.bind(this));
    }.bind(this));

    req.end();
    req.on('error', function(e) {
      error = "Request error:";
      this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error + " " + String(e)});
      utils.error(error, e);
    });

    this.dispatch(constants.ticket.LOOKUP_TICKET, ticket);
  };

  // extended details for claiming ticket, such as merkle proof
  this.lookupExtendedDetails = function(ticket, blockHash, live) {
    var options = {
      hostname: (live ? '' : 't') + 'btc.blockr.io',
      port: 443,
      path: '/api/v1/block/raw/' + blockHash,
      method: 'GET',
      withCredentials: false
    };

    var error;
    var req = https.request(options, function(res) {
      if (!res || res.statusCode !== 200) {
        error = "Error retrieving BTC block.";
        this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
        return;
      }

      res.on('data', function(data) {
        var json = JSON.parse(data);

        if (json.status !== 'success') {
          error = "Error retrieving BTC block data.";
          this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
          utils.error(error, json);
          return;
        }

        data = json.data;
        if (!data || !data.tx) {
          error = "Error: not enough data in BTC block.";
          this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error});
          return;
        }

        ticket.blockHash = (data.hash);

        var txIndex = _.indexOf(data.tx, ticket.txHash);

        var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

        var merkleProof = btcSwapClient.merkleProof(data.tx, txIndex);

        ticket.merkleProof = merkleProof;
        ticket.merkleProofStr = JSON.stringify(merkleProof);

        // Get btcrelay block validation fee for ticket.blockHash
        btcSwapClient.getFeeAmount("0x" + ticket.blockHash, function(fee) {
          ticket.blockFee = fee;
          ticket.formattedBlockFee = utils.formatEther(fee);
          this.dispatch(constants.ticket.LOOKUP_TICKET, ticket);
        }.bind(this), function(error) {
          utils.error(error);
        }.bind(this));
      }.bind(this));
    }.bind(this));

    req.end();
    req.on('error', function(e) {
      error = "Request error:";
      this.dispatch(constants.ticket.LOOKUP_TICKET_FAIL, {error: error + " " + String(e)});
      utils.error(error, e);
    });

    this.dispatch(constants.ticket.LOOKUP_TICKET, ticket);
  };

  this.createTicket = function(btcAddress, numEther, btcTotal) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.createTicket(btcAddress, numEther, btcTotal, function(result) {
      if (this.flux.stores.config.debug)
        utils.log('createTicket tx result:', result);

      var ticket = {
        id: '-',
        address: btcAddress,
        amount: bigRat(numEther).times(fixtures.ether),
        price: bigRat(btcTotal).divide(numEther).toDecimal(),
        total: btcTotal,
        expiry: 1,
        pendingHash: result,
        status: 'pending',
        owner: this.flux.stores.UserStore.user.id
      };
      this.dispatch(constants.ticket.CREATE_TICKET, ticket);
    }.bind(this), function(pendingHash, ticket) {
      if (this.flux.stores.config.debug) {
        utils.log('createTicket completed for ticket #', ticket.id);
        utils.log('pendingHash was', pendingHash);
      }

      this.dispatch(constants.ticket.CREATE_TICKET_SUCCESS, {
        pendingHash: pendingHash,
        ticket: ticket
      });
    }.bind(this), function(error) {
      utils.error('Ticket could not be created:', error);
      this.dispatch(constants.ticket.CREATE_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.cancelTicket = function(id) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.cancelTicket(id, function(ticketId, txHash) {
      if (this.flux.stores.config.debug) {
        utils.log('cancelTicket success for ticket #', ticketId);
        utils.log('cancelTicket tx result:', txHash);
      }
      this.dispatch(constants.ticket.CANCEL_TICKET, ticketId);
    }.bind(this), function(cancelledId) {
      if (this.flux.stores.config.debug)
        utils.log('cancelTicket completed for ticket #', cancelledId);

      // Let global watch remove ticket...
      // this.dispatch(constants.ticket.CANCEL_TICKET_SUCCESS, cancelledId);
    }.bind(this), function(error) {
      utils.error('Ticket could not be cancelled:', error);
      this.dispatch(constants.ticket.CANCEL_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.generateWallet = function() {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.generateWallet(function(wallet) {
      this.dispatch(constants.ticket.UPDATE_WALLET, wallet);
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.UPDATE_WALLET_FAIL, {error: error});
    }.bind(this));
  };

  this.importWallet = function(key) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.importWallet(key, function(wallet) {
      this.dispatch(constants.ticket.UPDATE_WALLET, wallet);
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.UPDATE_WALLET_FAIL, {error: error});
    }.bind(this));
  };

  this.clearWallet = function() {
    this.dispatch(constants.ticket.UPDATE_WALLET, {address: null, key: null, tx: null});
  };

  this.createTransaction = function(recipient, amount, fee) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();
    var wallet = this.flux.stores.TicketStore.wallet;
    var user = this.flux.stores.UserStore.user;

    btcSwapClient.createTransaction(wallet, recipient, amount, fee, user.id.substr(2), function(tx) {
      this.dispatch(constants.ticket.UPDATE_TX, tx);
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.UPDATE_TX_FAIL, {error: error});
    }.bind(this));
  };

  this.propagateTransaction = function(txHex) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.propagateTransaction(txHex, function(result) {
      this.dispatch(constants.ticket.PROPAGATE_TX, result);
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.UPDATE_TX_FAIL, {error: error});
    }.bind(this));
  };

  this.reserveTicket = function(id, txHash, powNonce) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.reserveTicket(id, txHash, powNonce, function(result) {
      if (this.flux.stores.config.debug)
        utils.log('reserveTicket result: ', result);

      this.dispatch(constants.ticket.RESERVE_TICKET, id);
    }.bind(this), function(reservedTicket) {
      if (this.flux.stores.config.debug)
        utils.log('reserveTicket completed for ticket #', reservedTicket.id);

      // Let global watch update ticket...
      this.dispatch(constants.ticket.RESERVE_TICKET_SUCCESS, reservedTicket);
    }.bind(this), function(error) {
      utils.error('Ticket could not be reserved:', error);
      this.dispatch(constants.ticket.RESERVE_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.claimTicket = function(id, txHex, txHash, txIndex, merkleSibling, txBlockHash, feeWei) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.claimTicket(id, txHex, txHash, txIndex, merkleSibling, txBlockHash, feeWei, function(result) {
      if (this.flux.stores.config.debug)
        utils.log('claimTicket result: ', result);

      this.dispatch(constants.ticket.CLAIM_TICKET, id);
    }.bind(this), function(claimedId) {
      if (this.flux.stores.config.debug)
        utils.log('claimTicket completed for ticket #', claimedId);

      // Let global watch remove ticket...
      this.dispatch(constants.ticket.CLAIM_TICKET_SUCCESS, claimedId);
    }.bind(this), function(error) {
      utils.error('Ticket could not be claimed:', error);
      this.dispatch(constants.ticket.CLAIM_TICKET_FAIL, {error: error});
    }.bind(this));
  };

  this.computePoW = function(id, txHash) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.computePoW(id, txHash, function(result) {
      this.dispatch(constants.ticket.UPDATE_POW, result);
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.COMPUTE_POW_FAIL, {error: error});
    }.bind(this));
  };

  this.verifyPoW = function(id, txHash, nonce) {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.verifyPoW(id, txHash, nonce, function(result) {
      this.dispatch(constants.ticket.VERIFY_POW, result);
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.VERIFY_POW_FAIL, {error: error});
    }.bind(this));
  };

  this.getBlockchainHead = function() {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.getBlockchainHead(function(result) {
      this.dispatch(constants.ticket.UPDATE_BTC_HEAD, {btcHead: result});

      btcSwapClient.getFeeAmount("0x" + result, function(fee) {
        this.dispatch(constants.ticket.UPDATE_BLOCK_FEE, {
          blockFee: fee,
          formattedBlockFee: utils.formatEther(fee)
        });
      }.bind(this), function(error) {
        utils.error(error);
      }.bind(this));
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.UPDATE_BTC_HEAD_FAIL, {error: error});
    }.bind(this));
  };

  this.getLastBlockHeight = function() {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();

    btcSwapClient.getLastBlockHeight(function(result) {
      // Check actual latest block height from 'https://btc.blockr.io/api/v1/block/info/last'
      var live = false;  // TODO live / testnet handling
      var options = {
        hostname: (live ? '' : 't') + 'btc.blockr.io',
        port: 443,
        path: '/api/v1/block/info/last',
        method: 'GET',
        withCredentials: false
      };

      var error;
      var req = https.request(options, function(res) {
        if (!res || res.statusCode !== 200) {
          error = "Error retrieving BTC block.";
          this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error});
          return;
        }

        res.on('data', function(data) {
          var json = JSON.parse(data);

          if (json.status !== 'success') {
            error = "Error retrieving BTC block height.";
            this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error});
            utils.error(error, json);
            return;
          }

          var blockData = json.data;
          this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT, {btcRealHeight: blockData.nb});
          this.dispatch(constants.ticket.UPDATE_BTC_HEAD, {btcRealHead: blockData.hash});
        }.bind(this));
      }.bind(this));

      req.end();
      req.on('error', function(e) {
        error = "Request error:";
        this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error + " " + String(e)});
        utils.error(error, e);
      }.bind(this));

      // hackish loop for now... FIXME
      setTimeout(function() {
        this.flux.actions.ticket.getLastBlockHeight();
        this.flux.actions.ticket.getBlockchainHead();
      }.bind(this), 30000);

      this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT, {btcHeight: _.parseInt(result)});
    }.bind(this), function(error) {
      utils.error(error);
      this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error});
    }.bind(this));
  };

  this.updateBlockHeader = function() {
    var btcSwapClient = this.flux.stores.config.getBtcSwapClient();
    var ticketState = this.flux.stores.TicketStore.getState();
    var feeWei = this.flux.stores.config.storeBlockFee;
    var blocksBehind = ticketState.btcBehind;

    if (!blocksBehind) {
      this.dispatch(constants.ticket.UPDATE_BTC_HEADER, false);
      return;
    }

    var live = false;  // TODO live / testnet handling
    var nextHeight = ticketState.btcHeight + 1;
    this.dispatch(constants.ticket.UPDATE_BTC_HEADER, nextHeight);

    var options = {
      hostname: (live ? '' : 't') + 'btc.blockr.io',
      port: 443,
      path: '/api/v1/block/info/' + nextHeight,
      method: 'GET',
      withCredentials: false
    };

    var error;
    var req = https.request(options, function(res) {
      if (!res || res.statusCode !== 200) {
        error = "Error retrieving BTC block.";
        this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error});
        return;
      }

      res.on('data', function(data) {
        var json = JSON.parse(data);

        if (json.status !== 'success') {
          error = "Error retrieving BTC block hash.";
          this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error});
          utils.error(error, json);
          return;
        }

        var blockData = json.data;

        if (this.flux.stores.config.debug)
          utils.log("BTC_BLOCK_HASH", blockData.hash);

        btcSwapClient.storeBlockWithFee(blockData.hash, feeWei, function(result) {
          if (result === blockData.nb) {
            this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT, {btcHeight: result});
            this.dispatch(constants.ticket.UPDATE_BTC_HEAD, {btcRealHead: blockData.hash});

            // Loop to next block
            setTimeout(function() {
              if (this.flux.stores.TicketStore.getState().btcHeight <= result)
                this.flux.actions.ticket.updateBlockHeader();
            }.bind(this), 1000);
          }
          else {
            error = `Block height mismatch, expected ${blockData.nb}, saw ${result}`;
            utils.error(error);
            this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error});
          }
        }.bind(this), function(err) {
          error = "Error storing block header:";
          utils.error(error, err);
          this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error + " " + String(err)});
        }.bind(this));

      }.bind(this));
    }.bind(this));

    req.end();
    req.on('error', function(e) {
      error = "Request error:";
      this.dispatch(constants.ticket.UPDATE_BTC_HEIGHT_FAIL, {error: error + " " + String(e)});
      utils.error(error, e);
    }.bind(this));
  };

  this.closeAlert = function() {
    this.dispatch(constants.ticket.CLOSE_ALERT);
  };
};

module.exports = TicketActions;