text:
<?php

namespace Holiday\Bundle\PeriodBundle\Entity;

use Doctrine\ORM\EntityRepository;

/**
 * PeriodRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class PeriodRepository extends EntityRepository
{
    public function retrieve(Array $params)
    {
        $qb = $this->createQueryBuilder("p");

        // Utilisateurs
        if ( isset($params['user']) ) {
            $ids = array();
            foreach ($params['user'] as $item) {
                $ids[] = $item->getId();
            }
            $qb->andWhere( $qb->expr()->in("p.user", $ids ));
        }

        // Status
        if ( isset($params['status']) ) {
            $ids = array();
            foreach ($params['status'] as $item) {
                $ids[] = $item->getId();
            }
            $qb->andWhere($qb->expr()->in("p.status", $ids ));
        }

        // Appartenance à un ou plusieurs groupes
        if ( isset($params['group']) ) {
            $ids = array();

            foreach ($params['group'] as $item) {
                $ids[] = $item;
            }
            $qb->innerJoin('p.user', 'u');
            $qb->innerJoin('u.groups', 'g');
            $qb->andWhere($qb->expr()->in("g.id", $ids ));
        }

        // Dates
        if ( !empty($params['from']) ) {
            $qb->andWhere("p.from>=:from OR p.to>=:from")->setParameter("from", new \DateTime($params['from']));
        }

        if ( !empty($params['to']) ) {
            $d = new \DateTime($params['to']);
            $qb->andWhere("p.to<=:to")->setParameter("to", $d->modify('+1day') );
        }

        // Ordonnacement
        if ( isset($params['order']) ) {
            $qb->addOrderBy($params['order'][0], $params['order'][1]);
        } else {
            $qb->addOrderBy('p.updatedAt', 'DESC');
        }

        $q = $qb->getQuery();

        try {
            return $q->getResult();
        } catch (\Doctrine\ORM\NoResultException $e) {
            return null;
        }
    }
}


text:
classdef MEAWithMicrodisplay < symphonyui.core.descriptions.RigDescription

    methods

        function obj = MEAWithMicrodisplay()
            import symphonyui.builtin.daqs.*;
            import symphonyui.builtin.devices.*;
            import symphonyui.core.*;
            import edu.washington.*;

            % Add the HEKA A/D board.
            daq = HekaDaqController();
            obj.daqController = daq;

            % Add the Multiclamp device (demo mode).
            amp1 = MultiClampDevice('Amp1', 1).bindStream(daq.getStream('ao0')).bindStream(daq.getStream('ai0'));
            obj.addDevice(amp1);

            % Add the LEDs.
            uvRamp = importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'uv_led_gamma_ramp.txt'));
            uv = CalibratedDevice('UV LED', Measurement.NORMALIZED, uvRamp(:, 1), uvRamp(:, 2)).bindStream(daq.getStream('ao1'));
            uv.addConfigurationSetting('ndfs', {}, ...
                'type', PropertyType('cellstr', 'row', {'C1', 'C2', 'C3', 'C4', 'C5'}));
            uv.addResource('ndfAttenuations', containers.Map( ...
                {'C1', 'C2', 'C3', 'C4', 'C5'}, ...
                {0.2768, 0.5076, 0.9281, 2.1275, 2.5022}));
            uv.addResource('fluxFactorPaths', containers.Map( ...
                {'none'}, {riekelab.Package.getCalibrationResource('rigs', 'mea', 'uv_led_flux_factors.txt')}));
            uv.addConfigurationSetting('lightPath', '', ...
                'type', PropertyType('char', 'row', {'', 'below', 'above'}));
            uv.addResource('spectrum', importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'uv_led_spectrum.txt')));
            obj.addDevice(uv);

            blueRamp = importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'blue_led_gamma_ramp.txt'));
            blue = CalibratedDevice('Blue LED', Measurement.NORMALIZED, blueRamp(:, 1), blueRamp(:, 2)).bindStream(daq.getStream('ao2'));
            blue.addConfigurationSetting('ndfs', {}, ...
                'type', PropertyType('cellstr', 'row', {'C1', 'C2', 'C3', 'C4', 'C5'}));
            blue.addResource('ndfAttenuations', containers.Map( ...
                {'C1', 'C2', 'C3', 'C4', 'C5'}, ...
                {0.2663, 0.5389, 0.9569, 2.0810, 2.3747}));
            blue.addResource('fluxFactorPaths', containers.Map( ...
                {'none'}, {riekelab.Package.getCalibrationResource('rigs', 'mea', 'blue_led_flux_factors.txt')}));
            blue.addConfigurationSetting('lightPath', '', ...
                'type', PropertyType('char', 'row', {'', 'below', 'above'}));
            blue.addResource('spectrum', importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'blue_led_spectrum.txt')));
            obj.addDevice(blue);

            greenRamp = importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'green_led_gamma_ramp.txt'));
            green = CalibratedDevice('Green LED', Measurement.NORMALIZED, greenRamp(:, 1), greenRamp(:, 2)).bindStream(daq.getStream('ao3'));
            green.addConfigurationSetting('ndfs', {}, ...
                'type', PropertyType('cellstr', 'row', {'C1', 'C2', 'C3', 'C4', 'C5'}));
            green.addResource('ndfAttenuations', containers.Map( ...
                {'C1', 'C2', 'C3', 'C4', 'C5'}, ...
                {0.2866, 0.5933, 0.9675, 1.9279, 2.1372}));
            green.addResource('fluxFactorPaths', containers.Map( ...
                {'none'}, {riekelab.Package.getCalibrationResource('rigs', 'mea', 'green_led_flux_factors.txt')}));
            green.addConfigurationSetting('lightPath', '', ...
                'type', PropertyType('char', 'row', {'', 'below', 'above'}));
            green.addResource('spectrum', importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'green_led_spectrum.txt')));
            obj.addDevice(green);

            % Add the Microdisplay
            ramps = containers.Map();
            ramps('minimum') = linspace(0, 65535, 256);
            ramps('low')     = 65535 * importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_low_gamma_ramp.txt'));
            ramps('medium')  = 65535 * importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_medium_gamma_ramp.txt'));
            ramps('high')    = 65535 * importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_high_gamma_ramp.txt'));
            ramps('maximum') = linspace(0, 65535, 256);
%             microdisplay = riekelab.devices.MicrodisplayDevice('gammaRamps', ramps, 'micronsPerPixel', 3.0, 'comPort', 'COM4');
            microdisplay = riekelab.devices.MicrodisplayDevice('gammaRamps', ramps, 'micronsPerPixel', 3.8, 'comPort', 'COM4', 'host', '192.168.0.102');
            microdisplay.bindStream(daq.getStream('doport1'));
            daq.getStream('doport1').setBitPosition(microdisplay, 15);
            microdisplay.addConfigurationSetting('ndfs', {}, ...
                'type', PropertyType('cellstr', 'row', {'E1', 'E2', 'E3', 'E4', 'E12'}));
            microdisplay.addResource('ndfAttenuations', containers.Map( ...
                {'white', 'red', 'green', 'blue'}, { ...
                containers.Map( ...
                    {'E1', 'E2', 'E3', 'E4', 'E12'}, ...
                    {0.26, 0.59, 0.94, 2.07, 0.30}), ...
                containers.Map( ...
                    {'E1', 'E2', 'E3', 'E4', 'E12'}, ...
                    {0.26, 0.61, 0.94, 2.05, 0.29}), ...
                containers.Map( ...
                    {'E1', 'E2', 'E3', 'E4', 'E12'}, ...
                    {0.26, 0.58, 0.94, 2.12, 0.29}), ...
                containers.Map( ...
                    {'E1', 'E2', 'E3', 'E4', 'E12'}, ...
                    {0.26, 0.57, 0.93, 2.13, 0.29})}));
            microdisplay.addResource('fluxFactorPaths', containers.Map( ...
                {'low', 'medium', 'high'}, { ...
                riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_low_flux_factors.txt'), ...
                riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_medium_flux_factors.txt'), ...
                riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_high_flux_factors.txt')}));
            microdisplay.addConfigurationSetting('lightPath', 'below', 'isReadOnly', true);
            microdisplay.addResource('spectrum', containers.Map( ...
                {'white', 'red', 'green', 'blue'}, { ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_white_spectrum.txt')), ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_red_spectrum.txt')), ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_green_spectrum.txt')), ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_blue_spectrum.txt'))}));

            % Get the quantal catch.
            myspect = containers.Map( ...
                {'white', 'red', 'green', 'blue'}, { ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_white_spectrum.txt')), ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_red_spectrum.txt')), ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_green_spectrum.txt')), ...
                importdata(riekelab.Package.getCalibrationResource('rigs', 'mea', 'microdisplay_below_blue_spectrum.txt'))});

            qCatch = zeros(3,4);
            names = {'red','green','blue'};
            for jj = 1 : length(names)
                q = myspect(names{jj});
%                 p = manookinlab.util.PhotoreceptorSpectrum( q(:, 1) );
%                 p = p / sum(p(1, :));
%                 qCatch(jj, :) = p * q(:, 2);
                qCatch(jj,:) = manookinlab.util.computeQuantalCatch(q(:, 1), q(:, 2));
            end
            microdisplay.addResource('quantalCatch', qCatch);

            obj.addDevice(microdisplay);

            % Add the frame monitor to record the timing of the monitor refresh.
            frameMonitor = UnitConvertingDevice('Frame Monitor', 'V').bindStream(daq.getStream('ai7'));
            obj.addDevice(frameMonitor);

            % Add a device for external triggering to synchronize MEA DAQ clock with Symphony DAQ clock.
            trigger = riekelab.devices.TriggerDevice();
            trigger.bindStream(daq.getStream('doport1'));
            daq.getStream('doport1').setBitPosition(trigger, 0);
            obj.addDevice(trigger);

            % Add the filter wheel.
            filterWheel = edu.washington.riekelab.devices.FilterWheelDevice('comPort', 'COM5');

            % Binding the filter wheel to an unused stream only so its configuration settings are written to each epoch.
            filterWheel.bindStream(daq.getStream('doport1'));
            daq.getStream('doport1').setBitPosition(filterWheel, 14);
            obj.addDevice(filterWheel);

            % Add the MEA device controller. This waits for the stream from Vision, strips of the header, and runs the block.
%             mea = manookinlab.devices.MEADevice('host', '192.168.0.100');
            mea = manookinlab.devices.MEADevice(9001);
            obj.addDevice(mea);
        end
    end
end

redpajama_set_name: RedPajamaGithub


text:
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "gmqcc.h"
#include "lexer.h"

#define HT_MACROS 1024

typedef struct {
    bool on;
    bool was_on;
    bool had_else;
} ppcondition;

typedef struct {
    int   token;
    char *value;
    /* a copy from the lexer */
    union {
        vec3_t v;
        int    i;
        double f;
        int    t; /* type */
    } constval;
} pptoken;

typedef struct {
    lex_ctx_t ctx;

    char   *name;
    char  **params;
    /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
    bool    has_params;
    bool    variadic;

    pptoken **output;
} ppmacro;

typedef struct ftepp_s {
    lex_file    *lex;
    int          token;
    unsigned int errors;

    bool         output_on;
    ppcondition *conditions;
    /*ppmacro    **macros;*/
    ht           macros;  /* hashtable<string, ppmacro*> */
    char        *output_string;

    char        *itemname;
    char        *includename;
    bool         in_macro;

    uint32_t predef_countval;
    uint32_t predef_randval;
} ftepp_t;

/* __DATE__ */
static char *ftepp_predef_date(ftepp_t *context) {
    const struct tm *itime = NULL;
    char            *value = (char*)mem_a(82);
    time_t           rtime;

    (void)context;

    time (&rtime);
    itime = util_localtime(&rtime);
    strftime(value, 82, "\"%b %d %Y\"", itime);

    return value;
}

/* __TIME__ */
static char *ftepp_predef_time(ftepp_t *context) {
    const struct tm *itime = NULL;
    char            *value = (char*)mem_a(82);
    time_t           rtime;

    (void)context;

    time (&rtime);
    itime = util_localtime(&rtime);
    strftime(value, 82, "\"%X\"", itime);

    return value;
}

/* __LINE__ */
static char *ftepp_predef_line(ftepp_t *context) {
    char *value;

    util_asprintf(&value, "%d", (int)context->lex->line);
    return value;
}
/* __FILE__ */
static char *ftepp_predef_file(ftepp_t *context) {
    size_t length = strlen(context->lex->name) + 3; /* two quotes and a terminator */
    char  *value  = (char*)mem_a(length);

    util_snprintf(value, length, "\"%s\"", context->lex->name);
    return value;
}
/* __COUNTER_LAST__ */
static char *ftepp_predef_counterlast(ftepp_t *context) {
    char *value;
    util_asprintf(&value, "%u", context->predef_countval);
    return value;
}
/* __COUNTER__ */
static char *ftepp_predef_counter(ftepp_t *context) {
    char *value;

    context->predef_countval ++;
    util_asprintf(&value, "%u", context->predef_countval);

    return value;
}
/* __RANDOM__ */
static char *ftepp_predef_random(ftepp_t *context) {
    char *value;

    context->predef_randval = (util_rand() % 0xFF) + 1;
    util_asprintf(&value, "%u", context->predef_randval);
    return value;
}
/* __RANDOM_LAST__ */
static char *ftepp_predef_randomlast(ftepp_t *context) {
    char *value;

    util_asprintf(&value, "%u", context->predef_randval);
    return value;
}
/* __TIMESTAMP__ */
static char *ftepp_predef_timestamp(ftepp_t *context) {
    struct stat finfo;
    const char *find;
    char       *value;
    size_t      size;

    if (stat(context->lex->name, &finfo))
        return util_strdup("\"<failed to determine timestamp>\"");

    find = util_ctime(&finfo.st_mtime);
    value = (char*)mem_a(strlen(find) + 1);
    memcpy(&value[1], find, (size = strlen(find)) - 1);

    value[0]    = '"';
    value[size] = '"';

    return value;
}

typedef struct {
    const char   *name;
    char       *(*func)(ftepp_t *);
} ftepp_predef_t;

static const ftepp_predef_t ftepp_predefs[] = {
    { "__LINE__",         &ftepp_predef_line        },
    { "__FILE__",         &ftepp_predef_file        },
    { "__COUNTER__",      &ftepp_predef_counter     },
    { "__COUNTER_LAST__", &ftepp_predef_counterlast },
    { "__RANDOM__",       &ftepp_predef_random      },
    { "__RANDOM_LAST__",  &ftepp_predef_randomlast  },
    { "__DATE__",         &ftepp_predef_date        },
    { "__TIME__",         &ftepp_predef_time        },
    { "__TIME_STAMP__",   &ftepp_predef_timestamp   }
};

static GMQCC_INLINE size_t ftepp_predef_index(const char *name) {
    /* no hashtable here, we simply check for one to exist the naive way */
    size_t i;
    for(i = 1; i < GMQCC_ARRAY_COUNT(ftepp_predefs) + 1; i++)
        if (!strcmp(ftepp_predefs[i-1].name, name))
            return i;
    return 0;
}

bool ftepp_predef_exists(const char *name);
bool ftepp_predef_exists(const char *name) {
    return ftepp_predef_index(name) != 0;
}

/* singleton because we're allowed */
static GMQCC_INLINE char *(*ftepp_predef(const char *name))(ftepp_t *context) {
    size_t i = ftepp_predef_index(name);
    return (i != 0) ? ftepp_predefs[i-1].func : NULL;
}

#define ftepp_tokval(f) ((f)->lex->tok.value)
#define ftepp_ctx(f)    ((f)->lex->tok.ctx)

static void ftepp_errorat(ftepp_t *ftepp, lex_ctx_t ctx, const char *fmt, ...)
{
    va_list ap;

    ftepp->errors++;

    va_start(ap, fmt);
    con_cvprintmsg(ctx, LVL_ERROR, "error", fmt, ap);
    va_end(ap);
}

static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
{
    va_list ap;

    ftepp->errors++;

    va_start(ap, fmt);
    con_cvprintmsg(ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
    va_end(ap);
}

static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
{
    bool    r;
    va_list ap;

    va_start(ap, fmt);
    r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
    va_end(ap);
    return r;
}

static pptoken *pptoken_make(ftepp_t *ftepp)
{
    pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
    token->token = ftepp->token;
#if 0
    if (token->token == TOKEN_WHITE)
        token->value = util_strdup(" ");
    else
#else
        token->value = util_strdup(ftepp_tokval(ftepp));
#endif
    memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
    return token;
}

static GMQCC_INLINE void pptoken_delete(pptoken *self)
{
    mem_d(self->value);
    mem_d(self);
}

static ppmacro *ppmacro_new(lex_ctx_t ctx, const char *name)
{
    ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));

    (void)ctx;
    memset(macro, 0, sizeof(*macro));
    macro->name = util_strdup(name);
    return macro;
}

static void ppmacro_delete(ppmacro *self)
{
    size_t i;
    for (i = 0; i < vec_size(self->params); ++i)
        mem_d(self->params[i]);
    vec_free(self->params);
    for (i = 0; i < vec_size(self->output); ++i)
        pptoken_delete(self->output[i]);
    vec_free(self->output);
    mem_d(self->name);
    mem_d(self);
}

static ftepp_t* ftepp_new(void)
{
    ftepp_t *ftepp;

    ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
    memset(ftepp, 0, sizeof(*ftepp));

    ftepp->macros          = util_htnew(HT_MACROS);
    ftepp->output_on       = true;
    ftepp->predef_countval = 0;
    ftepp->predef_randval  = 0;

    return ftepp;
}

static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
{
    vec_free(self->output_string);
}

static void ftepp_delete(ftepp_t *self)
{
    ftepp_flush_do(self);
    if (self->itemname)
        mem_d(self->itemname);
    if (self->includename)
        vec_free(self->includename);

    util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);

    vec_free(self->conditions);
    if (self->lex)
        lex_close(self->lex);
    mem_d(self);
}

static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
{
    if (ignore_cond || ftepp->output_on)
    {
        size_t len;
        char  *data;
        len = strlen(str);
        data = vec_add(ftepp->output_string, len);
        memcpy(data, str, len);
    }
}

static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
{
    size_t i;
    ftepp->output_on = true;
    for (i = 0; i < vec_size(ftepp->conditions); ++i)
        ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
}

static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
{
    return (ppmacro*)util_htget(ftepp->macros, name);
}

static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
{
    util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
}

static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
{
    return (ftepp->token = lex_do(ftepp->lex));
}

/* Important: this does not skip newlines! */
static bool ftepp_skipspace(ftepp_t *ftepp)
{
    if (ftepp->token != TOKEN_WHITE)
        return true;
    while (ftepp_next(ftepp) == TOKEN_WHITE) {}
    if (ftepp->token >= TOKEN_EOF) {
        ftepp_error(ftepp, "unexpected end of preprocessor directive");
        return false;
    }
    return true;
}

/* this one skips EOLs as well */
static bool ftepp_skipallwhite(ftepp_t *ftepp)
{
    if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
        return true;
    do {
        ftepp_next(ftepp);
    } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
    if (ftepp->token >= TOKEN_EOF) {
        ftepp_error(ftepp, "unexpected end of preprocessor directive");
        return false;
    }
    return true;
}

/**
 * The huge macro parsing code...
 */
static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
{
    do {
        ftepp_next(ftepp);
        if (!ftepp_skipspace(ftepp))
            return false;
        if (ftepp->token == ')')
            break;
        switch (ftepp->token) {
            case TOKEN_IDENT:
            case TOKEN_TYPENAME:
            case TOKEN_KEYWORD:
                vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
                break;
            case TOKEN_DOTS:
                macro->variadic = true;
                break;
            default:
                ftepp_error(ftepp, "unexpected token in parameter list");
                return false;
        }
        ftepp_next(ftepp);
        if (!ftepp_skipspace(ftepp))
            return false;
        if (macro->variadic && ftepp->token != ')') {
            ftepp_error(ftepp, "cannot have parameters after the variadic parameters");
            return false;
        }
    } while (ftepp->token == ',');

    if (ftepp->token != ')') {
        ftepp_error(ftepp, "expected closing paren after macro parameter list");
        return false;
    }
    ftepp_next(ftepp);
    /* skipspace happens in ftepp_define */
    return true;
}

static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
{
    pptoken *ptok;
    while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
        bool   subscript = false;
        size_t index     = 0;
        if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
            subscript = !!(ftepp_next(ftepp) == '#');

            if (subscript && ftepp_next(ftepp) != '#') {
                ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
                return false;
            } else if (subscript) {
                if (ftepp_next(ftepp) == '[') {
                    if (ftepp_next(ftepp) != TOKEN_INTCONST) {
                        ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
                        return false;
                    }

                    index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);

                    if (ftepp_next(ftepp) != ']') {
                        ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
                        return false;
                    }

                    /*
                     * mark it as an array to be handled later as such and not
                     * as traditional __VA_ARGS__
                     */
                    ftepp->token = TOKEN_VA_ARGS_ARRAY;
                    ptok = pptoken_make(ftepp);
                    ptok->constval.i = index;
                    vec_push(macro->output, ptok);
                    ftepp_next(ftepp);
                } else {
                    ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
                    return false;
                }
            } else {
                int old = ftepp->token;
                ftepp->token = TOKEN_VA_ARGS;
                ptok = pptoken_make(ftepp);
                vec_push(macro->output, ptok);
                ftepp->token = old;
            }
        }
        else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) {
            ftepp->token = TOKEN_VA_COUNT;
            ptok         = pptoken_make(ftepp);
            vec_push(macro->output, ptok);
            ftepp_next(ftepp);
        } else {
            ptok = pptoken_make(ftepp);
            vec_push(macro->output, ptok);
            ftepp_next(ftepp);
        }
    }
    /* recursive expansion can cause EOFs here */
    if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
        ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
        return false;
    }
    return true;
}

static const char *ftepp_math_constants[][2] = {
    { "M_E",        "2.7182818284590452354"  }, /* e          */
    { "M_LOG2E",    "1.4426950408889634074"  }, /* log_2 e    */
    { "M_LOG10E",   "0.43429448190325182765" }, /* log_10 e   */
    { "M_LN2",      "0.69314718055994530942" }, /* log_e 2    */
    { "M_LN10",     "2.30258509299404568402" }, /* log_e 10   */
    { "M_PI",       "3.14159265358979323846" }, /* pi         */
    { "M_PI_2",     "1.57079632679489661923" }, /* pi/2       */
    { "M_PI_4",     "0.78539816339744830962" }, /* pi/4       */
    { "M_1_PI",     "0.31830988618379067154" }, /* 1/pi       */
    { "M_2_PI",     "0.63661977236758134308" }, /* 2/pi       */
    { "M_2_SQRTPI", "1.12837916709551257390" }, /* 2/sqrt(pi) */
    { "M_SQRT2",    "1.41421356237309504880" }, /* sqrt(2)    */
    { "M_SQRT1_2",  "0.70710678118654752440" }, /* 1/sqrt(2)  */
    { "M_TAU",      "6.28318530717958647692" }  /* pi*2       */
};

static bool ftepp_define(ftepp_t *ftepp)
{
    ppmacro *macro = NULL;
    size_t l = ftepp_ctx(ftepp).line;
    size_t i;
    bool   mathconstant = false;

    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;

    switch (ftepp->token) {
        case TOKEN_IDENT:
        case TOKEN_TYPENAME:
        case TOKEN_KEYWORD:
            if (OPTS_FLAG(FTEPP_MATHDEFS)) {
                for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++) {
                    if (!strcmp(ftepp_math_constants[i][0], ftepp_tokval(ftepp))) {
                        mathconstant = true;
                        break;
                    }
                }
            }

            macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));

            if (OPTS_FLAG(FTEPP_MATHDEFS)) {
                /* user defined ones take precedence */
                if (macro && mathconstant) {
                    ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
                    macro = NULL;
                }
            }

            if (macro && ftepp->output_on) {
                if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
                    return false;
                ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
            }
            macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
            break;
        default:
            ftepp_error(ftepp, "expected macro name");
            return false;
    }

    (void)ftepp_next(ftepp);

    if (ftepp->token == '(') {
        macro->has_params = true;
        if (!ftepp_define_params(ftepp, macro)) {
            ppmacro_delete(macro);
            return false;
        }
    }

    if (!ftepp_skipspace(ftepp)) {
        ppmacro_delete(macro);
        return false;
    }

    if (!ftepp_define_body(ftepp, macro)) {
        ppmacro_delete(macro);
        return false;
    }

    if (ftepp->output_on)
        util_htset(ftepp->macros, macro->name, (void*)macro);
    else {
        ppmacro_delete(macro);
    }

    for (; l < ftepp_ctx(ftepp).line; ++l)
        ftepp_out(ftepp, "\n", true);
    return true;
}

/**
 * When a macro is used we have to handle parameters as well
 * as special-concatenation via ## or stringification via #
 *
 * Note: parenthesis can nest, so FOO((a),b) is valid, but only
 * this kind of parens. Curly braces or [] don't count towards the
 * paren-level.
 */
typedef struct {
    pptoken **tokens;
} macroparam;

static void macroparam_clean(macroparam *self)
{
    size_t i;
    for (i = 0; i < vec_size(self->tokens); ++i)
        pptoken_delete(self->tokens[i]);
    vec_free(self->tokens);
}

/* need to leave the last token up */
static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
{
    macroparam *params = NULL;
    pptoken    *ptok;
    macroparam  mp;
    size_t      parens = 0;
    size_t      i;

    if (!ftepp_skipallwhite(ftepp))
        return false;
    while (ftepp->token != ')') {
        mp.tokens = NULL;
        if (!ftepp_skipallwhite(ftepp))
            return false;
        while (parens || ftepp->token != ',') {
            if (ftepp->token == '(')
                ++parens;
            else if (ftepp->token == ')') {
                if (!parens)
                    break;
                --parens;
            }
            ptok = pptoken_make(ftepp);
            vec_push(mp.tokens, ptok);
            if (ftepp_next(ftepp) >= TOKEN_EOF) {
                ftepp_error(ftepp, "unexpected end of file in macro call");
                goto on_error;
            }
        }
        vec_push(params, mp);
        mp.tokens = NULL;
        if (ftepp->token == ')')
            break;
        if (ftepp->token != ',') {
            ftepp_error(ftepp, "expected closing paren or comma in macro call");
            goto on_error;
        }
        if (ftepp_next(ftepp) >= TOKEN_EOF) {
            ftepp_error(ftepp, "unexpected end of file in macro call");
            goto on_error;
        }
    }
    *out_params = params;
    return true;

on_error:
    if (mp.tokens)
        macroparam_clean(&mp);
    for (i = 0; i < vec_size(params); ++i)
        macroparam_clean(&params[i]);
    vec_free(params);
    return false;
}

static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
{
    size_t i;
    for (i = 0; i < vec_size(macro->params); ++i) {
        if (!strcmp(macro->params[i], name)) {
            *idx = i;
            return true;
        }
    }
    return false;
}

static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
{
    char        chs[2];
    const char *ch;
    chs[1] = 0;
    switch (token->token) {
        case TOKEN_STRINGCONST:
            ch = token->value;
            while (*ch) {
                /* in preprocessor mode strings already are string,
                 * so we don't get actual newline bytes here.
                 * Still need to escape backslashes and quotes.
                 */
                switch (*ch) {
                    case '\\': ftepp_out(ftepp, "\\\\", false); break;
                    case '"':  ftepp_out(ftepp, "\\\"", false); break;
                    default:
                        chs[0] = *ch;
                        ftepp_out(ftepp, chs, false);
                        break;
                }
                ++ch;
            }
            break;
        case TOKEN_WHITE:
            ftepp_out(ftepp, " ", false);
            break;
        case TOKEN_EOL:
            ftepp_out(ftepp, "\\n", false);
            break;
        default:
            ftepp_out(ftepp, token->value, false);
            break;
    }
}

static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
{
    size_t i;
    ftepp_out(ftepp, "\"", false);
    for (i = 0; i < vec_size(param->tokens); ++i)
        ftepp_stringify_token(ftepp, param->tokens[i]);
    ftepp_out(ftepp, "\"", false);
}

static void ftepp_recursion_header(ftepp_t *ftepp)
{
    ftepp_out(ftepp, "\n#pragma push(line)\n", false);
}

static void ftepp_recursion_footer(ftepp_t *ftepp)
{
    ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
}

static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
{
    size_t   i;
    pptoken *out;
    for (i = 0; i < vec_size(param->tokens); ++i) {
        out = param->tokens[i];
        if (out->token == TOKEN_EOL)
            ftepp_out(ftepp, "\n", false);
        else
            ftepp_out(ftepp, out->value, false);
    }
}

static bool ftepp_preprocess(ftepp_t *ftepp);
static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
{
    char     *buffer       = NULL;
    char     *old_string   = ftepp->output_string;
    char     *inner_string;
    lex_file *old_lexer    = ftepp->lex;
    size_t    vararg_start = vec_size(macro->params);
    bool      retval       = true;
    bool      has_newlines;
    size_t    varargs;

    size_t    o, pi;
    lex_file *inlex;

    bool      old_inmacro;

    int nextok;

    if (vararg_start < vec_size(params))
        varargs = vec_size(params) - vararg_start;
    else
        varargs = 0;

    /* really ... */
    if (!vec_size(macro->output))
        return true;

    ftepp->output_string = NULL;
    for (o = 0; o < vec_size(macro->output); ++o) {
        pptoken *out = macro->output[o];
        switch (out->token) {
            case TOKEN_VA_ARGS:
                if (!macro->variadic) {
                    ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
                    vec_free(old_string);
                    return false;
                }
                if (!varargs)
                    break;

                pi = 0;
                ftepp_param_out(ftepp, &params[pi + vararg_start]);
                for (++pi; pi < varargs; ++pi) {
                    ftepp_out(ftepp, ", ", false);
                    ftepp_param_out(ftepp, &params[pi + vararg_start]);
                }
                break;

            case TOKEN_VA_ARGS_ARRAY:
                if ((size_t)out->constval.i >= varargs) {
                    ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
                    vec_free(old_string);
                    return false;
                }

                ftepp_param_out(ftepp, &params[out->constval.i + vararg_start]);
                break;

            case TOKEN_VA_COUNT:
                util_asprintf(&buffer, "%d", varargs);
                ftepp_out(ftepp, buffer, false);
                mem_d(buffer);
                break;

            case TOKEN_IDENT:
            case TOKEN_TYPENAME:
            case TOKEN_KEYWORD:
                if (!macro_params_find(macro, out->value, &pi)) {
                    ftepp_out(ftepp, out->value, false);
                    break;
                } else
                    ftepp_param_out(ftepp, &params[pi]);
                break;
            case '#':
                if (o + 1 < vec_size(macro->output)) {
                    nextok = macro->output[o+1]->token;
                    if (nextok == '#') {
                        /* raw concatenation */
                        ++o;
                        break;
                    }
                    if ( (nextok == TOKEN_IDENT    ||
                          nextok == TOKEN_KEYWORD  ||
                          nextok == TOKEN_TYPENAME) &&
                        macro_params_find(macro, macro->output[o+1]->value, &pi))
                    {
                        ++o;
                        ftepp_stringify(ftepp, &params[pi]);
                        break;
                    }
                }
                ftepp_out(ftepp, "#", false);
                break;
            case TOKEN_EOL:
                ftepp_out(ftepp, "\n", false);
                break;
            default:
                ftepp_out(ftepp, out->value, false);
                break;
        }
    }
    vec_push(ftepp->output_string, 0);
    /* Now run the preprocessor recursively on this string buffer */
    /*
    printf("__________\n%s\n=========\n", ftepp->output_string);
    */
    inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
    if (!inlex) {
        ftepp_error(ftepp, "internal error: failed to instantiate lexer");
        retval = false;
        goto cleanup;
    }

    inlex->line  = ftepp->lex->line;
    inlex->sline = ftepp->lex->sline;
    ftepp->lex   = inlex;

    old_inmacro     = ftepp->in_macro;
    ftepp->in_macro = true;
    ftepp->output_string = NULL;
    if (!ftepp_preprocess(ftepp)) {
        ftepp->in_macro = old_inmacro;
        vec_free(ftepp->lex->open_string);
        vec_free(ftepp->output_string);
        lex_close(ftepp->lex);
        retval = false;
        goto cleanup;
    }
    ftepp->in_macro = old_inmacro;
    vec_free(ftepp->lex->open_string);
    lex_close(ftepp->lex);

    inner_string = ftepp->output_string;
    ftepp->output_string = old_string;

    has_newlines = (strchr(inner_string, '\n') != NULL);

    if (has_newlines && !old_inmacro)
        ftepp_recursion_header(ftepp);

    vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
    vec_free(inner_string);

    if (has_newlines && !old_inmacro)
        ftepp_recursion_footer(ftepp);

    if (resetline && !ftepp->in_macro) {
        char lineno[128];
        util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
        ftepp_out(ftepp, lineno, false);
    }

    old_string = ftepp->output_string;
cleanup:
    ftepp->lex           = old_lexer;
    ftepp->output_string = old_string;
    return retval;
}

static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
{
    size_t     o;
    macroparam *params = NULL;
    bool        retval = true;
    size_t      paramline;

    if (!macro->has_params) {
        if (!ftepp_macro_expand(ftepp, macro, NULL, false))
            return false;
        ftepp_next(ftepp);
        return true;
    }
    ftepp_next(ftepp);

    if (!ftepp_skipallwhite(ftepp))
        return false;

    if (ftepp->token != '(') {
        ftepp_error(ftepp, "expected macro parameters in parenthesis");
        return false;
    }

    ftepp_next(ftepp);
    paramline = ftepp->lex->sline;
    if (!ftepp_macro_call_params(ftepp, &params))
        return false;

    if ( vec_size(params) < vec_size(macro->params) ||
        (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
    {
        ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
                    (macro->variadic ? " at least" : ""),
                    (unsigned int)vec_size(macro->params),
                    (unsigned int)vec_size(params));
        retval = false;
        goto cleanup;
    }

    if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
        retval = false;
    ftepp_next(ftepp);

cleanup:
    for (o = 0; o < vec_size(params); ++o)
        macroparam_clean(&params[o]);
    vec_free(params);
    return retval;
}

/**
 * #if - the FTEQCC way:
 *    defined(FOO) => true if FOO was #defined regardless of parameters or contents
 *    <numbers>    => True if the number is not 0
 *    !<factor>    => True if the factor yields false
 *    !!<factor>   => ERROR on 2 or more unary nots
 *    <macro>      => becomes the macro's FIRST token regardless of parameters
 *    <e> && <e>   => True if both expressions are true
 *    <e> || <e>   => True if either expression is true
 *    <string>     => False
 *    <ident>      => False (remember for macros the <macro> rule applies instead)
 * Unary + and - are weird and wrong in fteqcc so we don't allow them
 * parenthesis in expressions are allowed
 * parameter lists on macros are errors
 * No mathematical calculations are executed
 */
static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out);
static bool ftepp_if_op(ftepp_t *ftepp)
{
    ftepp->lex->flags.noops = false;
    ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;
    ftepp->lex->flags.noops = true;
    return true;
}
static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
{
    ppmacro *macro;
    bool     wasnot = false;
    bool     wasneg = false;

    if (!ftepp_skipspace(ftepp))
        return false;

    while (ftepp->token == '!') {
        wasnot = true;
        ftepp_next(ftepp);
        if (!ftepp_skipspace(ftepp))
            return false;
    }

    if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-"))
    {
        wasneg = true;
        ftepp_next(ftepp);
        if (!ftepp_skipspace(ftepp))
            return false;
    }

    switch (ftepp->token) {
        case TOKEN_IDENT:
        case TOKEN_TYPENAME:
        case TOKEN_KEYWORD:
            if (!strcmp(ftepp_tokval(ftepp), "defined")) {
                ftepp_next(ftepp);
                if (!ftepp_skipspace(ftepp))
                    return false;
                if (ftepp->token != '(') {
                    ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
                    return false;
                }
                ftepp_next(ftepp);
                if (!ftepp_skipspace(ftepp))
                    return false;
                if (ftepp->token != TOKEN_IDENT &&
                    ftepp->token != TOKEN_TYPENAME &&
                    ftepp->token != TOKEN_KEYWORD)
                {
                    ftepp_error(ftepp, "defined() used on an unexpected token type");
                    return false;
                }
                macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
                *out = !!macro;
                ftepp_next(ftepp);
                if (!ftepp_skipspace(ftepp))
                    return false;
                if (ftepp->token != ')') {
                    ftepp_error(ftepp, "expected closing paren");
                    return false;
                }
                break;
            }

            macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
            if (!macro || !vec_size(macro->output)) {
                *out = false;
                *value_out = 0;
            } else {
                /* This does not expand recursively! */
                switch (macro->output[0]->token) {
                    case TOKEN_INTCONST:
                        *value_out = macro->output[0]->constval.i;
                        *out = !!(macro->output[0]->constval.i);
                        break;
                    case TOKEN_FLOATCONST:
                        *value_out = macro->output[0]->constval.f;
                        *out = !!(macro->output[0]->constval.f);
                        break;
                    default:
                        *out = false;
                        break;
                }
            }
            break;
        case TOKEN_STRINGCONST:
            *value_out = 0;
            *out = false;
            break;
        case TOKEN_INTCONST:
            *value_out = ftepp->lex->tok.constval.i;
            *out = !!(ftepp->lex->tok.constval.i);
            break;
        case TOKEN_FLOATCONST:
            *value_out = ftepp->lex->tok.constval.f;
            *out = !!(ftepp->lex->tok.constval.f);
            break;

        case '(':
            ftepp_next(ftepp);
            if (!ftepp_if_expr(ftepp, out, value_out))
                return false;
            if (ftepp->token != ')') {
                ftepp_error(ftepp, "expected closing paren in #if expression");
                return false;
            }
            break;

        default:
            ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
            if (OPTS_OPTION_BOOL(OPTION_DEBUG))
                ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
            return false;
    }
    if (wasneg)
        *value_out = -*value_out;
    if (wasnot) {
        *out = !*out;
        *value_out = (*out ? 1 : 0);
    }
    return true;
}

/*
static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
{
    if (!ftepp_next(ftepp))
        return false;
    return ftepp_if_value(ftepp, out, value_out);
}
*/

static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
{
    if (!ftepp_if_value(ftepp, out, value_out))
        return false;

    if (!ftepp_if_op(ftepp))
        return false;

    if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
        return true;

    /* FTEQCC is all right-associative and no precedence here */
    if (!strcmp(ftepp_tokval(ftepp), "&&") ||
        !strcmp(ftepp_tokval(ftepp), "||"))
    {
        bool next = false;
        char opc  = ftepp_tokval(ftepp)[0];
        double nextvalue;

        (void)nextvalue;
        if (!ftepp_next(ftepp))
            return false;
        if (!ftepp_if_expr(ftepp, &next, &nextvalue))
            return false;

        if (opc == '&')
            *out = *out && next;
        else
            *out = *out || next;

        *value_out = (*out ? 1 : 0);
        return true;
    }
    else if (!strcmp(ftepp_tokval(ftepp), "==") ||
             !strcmp(ftepp_tokval(ftepp), "!=") ||
             !strcmp(ftepp_tokval(ftepp), ">=") ||
             !strcmp(ftepp_tokval(ftepp), "<=") ||
             !strcmp(ftepp_tokval(ftepp), ">") ||
             !strcmp(ftepp_tokval(ftepp), "<"))
    {
        bool next = false;
        const char opc0 = ftepp_tokval(ftepp)[0];
        const char opc1 = ftepp_tokval(ftepp)[1];
        double other;

        if (!ftepp_next(ftepp))
            return false;
        if (!ftepp_if_expr(ftepp, &next, &other))
            return false;

        if (opc0 == '=')
            *out = (*value_out == other);
        else if (opc0 == '!')
            *out = (*value_out != other);
        else if (opc0 == '>') {
            if (opc1 == '=') *out = (*value_out >= other);
            else             *out = (*value_out > other);
        }
        else if (opc0 == '<') {
            if (opc1 == '=') *out = (*value_out <= other);
            else             *out = (*value_out < other);
        }
        *value_out = (*out ? 1 : 0);

        return true;
    }
    else {
        ftepp_error(ftepp, "junk after #if");
        return false;
    }
}

static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
{
    bool result = false;
    double dummy = 0;

    memset(cond, 0, sizeof(*cond));
    (void)ftepp_next(ftepp);

    if (!ftepp_skipspace(ftepp))
        return false;
    if (ftepp->token == TOKEN_EOL) {
        ftepp_error(ftepp, "expected expression for #if-directive");
        return false;
    }

    if (!ftepp_if_expr(ftepp, &result, &dummy))
        return false;

    cond->on = result;
    return true;
}

/**
 * ifdef is rather simple
 */
static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
{
    ppmacro *macro;
    memset(cond, 0, sizeof(*cond));
    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;

    switch (ftepp->token) {
        case TOKEN_IDENT:
        case TOKEN_TYPENAME:
        case TOKEN_KEYWORD:
            macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
            break;
        default:
            ftepp_error(ftepp, "expected macro name");
            return false;
    }

    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;
    /* relaxing this condition
    if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
        ftepp_error(ftepp, "stray tokens after #ifdef");
        return false;
    }
    */
    cond->on = !!macro;
    return true;
}

/**
 * undef is also simple
 */
static bool ftepp_undef(ftepp_t *ftepp)
{
    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;

    if (ftepp->output_on) {
        switch (ftepp->token) {
            case TOKEN_IDENT:
            case TOKEN_TYPENAME:
            case TOKEN_KEYWORD:
                ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
                break;
            default:
                ftepp_error(ftepp, "expected macro name");
                return false;
        }
    }

    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;
    /* relaxing this condition
    if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
        ftepp_error(ftepp, "stray tokens after #ifdef");
        return false;
    }
    */
    return true;
}

/* Special unescape-string function which skips a leading quote
 * and stops at a quote, not just at \0
 */
static void unescape(const char *str, char *out) {
    ++str;
    while (*str && *str != '"') {
        if (*str == '\\') {
            ++str;
            switch (*str) {
                case '\\': *out++ = *str; break;
                case '"':  *out++ = *str; break;
                case 'a':  *out++ = '\a'; break;
                case 'b':  *out++ = '\b'; break;
                case 'r':  *out++ = '\r'; break;
                case 'n':  *out++ = '\n'; break;
                case 't':  *out++ = '\t'; break;
                case 'f':  *out++ = '\f'; break;
                case 'v':  *out++ = '\v'; break;
                default:
                    *out++ = '\\';
                    *out++ = *str;
                    break;
            }
            ++str;
            continue;
        }

        *out++ = *str++;
    }
    *out = 0;
}

static char *ftepp_include_find_path(const char *file, const char *pathfile)
{
    fs_file_t  *fp;
    char       *filename = NULL;
    const char *last_slash;
    size_t      len;

    if (!pathfile)
        return NULL;

    last_slash = strrchr(pathfile, '/');

    if (last_slash) {
        len = last_slash - pathfile;
        memcpy(vec_add(filename, len), pathfile, len);
        vec_push(filename, '/');
    }

    len = strlen(file);
    memcpy(vec_add(filename, len+1), file, len);
    vec_last(filename) = 0;

    fp = fs_file_open(filename, "rb");
    if (fp) {
        fs_file_close(fp);
        return filename;
    }
    vec_free(filename);
    return NULL;
}

static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
{
    char *filename = NULL;

    filename = ftepp_include_find_path(file, ftepp->includename);
    if (!filename)
        filename = ftepp_include_find_path(file, ftepp->itemname);
    return filename;
}

static bool ftepp_directive_warning(ftepp_t *ftepp) {
    char *message = NULL;

    if (!ftepp_skipspace(ftepp))
        return false;

    /* handle the odd non string constant case so it works like C */
    if (ftepp->token != TOKEN_STRINGCONST) {
        bool  store   = false;
        vec_append(message, 8, "#warning");
        ftepp_next(ftepp);
        while (ftepp->token != TOKEN_EOL) {
            vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
            ftepp_next(ftepp);
        }
        vec_push(message, '\0');
        if (ftepp->output_on)
            store = ftepp_warn(ftepp, WARN_CPP, message);
        else
            store = false;
        vec_free(message);
        return store;
    }

    if (!ftepp->output_on)
        return false;

    unescape  (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
    return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
}

static void ftepp_directive_error(ftepp_t *ftepp) {
    char *message = NULL;

    if (!ftepp_skipspace(ftepp))
        return;

    /* handle the odd non string constant case so it works like C */
    if (ftepp->token != TOKEN_STRINGCONST) {
        vec_append(message, 6, "#error");
        ftepp_next(ftepp);
        while (ftepp->token != TOKEN_EOL) {
            vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
            ftepp_next(ftepp);
        }
        vec_push(message, '\0');
        if (ftepp->output_on)
            ftepp_error(ftepp, message);
        vec_free(message);
        return;
    }

    if (!ftepp->output_on)
        return;

    unescape  (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
    ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
}

static void ftepp_directive_message(ftepp_t *ftepp) {
    char *message = NULL;

    if (!ftepp_skipspace(ftepp))
        return;

    /* handle the odd non string constant case so it works like C */
    if (ftepp->token != TOKEN_STRINGCONST) {
        vec_append(message, 8, "#message");
        ftepp_next(ftepp);
        while (ftepp->token != TOKEN_EOL) {
            vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
            ftepp_next(ftepp);
        }
        vec_push(message, '\0');
        if (ftepp->output_on)
            con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", message);
        vec_free(message);
        return;
    }

    if (!ftepp->output_on)
        return;

    unescape     (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
    con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message",  ftepp_tokval(ftepp));
}

/**
 * Include a file.
 * FIXME: do we need/want a -I option?
 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
 */
static bool ftepp_include(ftepp_t *ftepp)
{
    lex_file *old_lexer = ftepp->lex;
    lex_file *inlex;
    lex_ctx_t ctx;
    char     lineno[128];
    char     *filename;
    char     *old_includename;

    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;

    if (ftepp->token != TOKEN_STRINGCONST) {
        ftepp_error(ftepp, "expected filename to include");
        return false;
    }

    if (!ftepp->output_on) {
        ftepp_next(ftepp);
        return true;
    }

    ctx = ftepp_ctx(ftepp);

    unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));

    ftepp_out(ftepp, "\n#pragma file(", false);
    ftepp_out(ftepp, ftepp_tokval(ftepp), false);
    ftepp_out(ftepp, ")\n#pragma line(1)\n", false);

    filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
    if (!filename) {
        ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp));
        return false;
    }
    inlex = lex_open(filename);
    if (!inlex) {
        ftepp_error(ftepp, "open failed on include file `%s`", filename);
        vec_free(filename);
        return false;
    }
    ftepp->lex = inlex;
    old_includename = ftepp->includename;
    ftepp->includename = filename;
    if (!ftepp_preprocess(ftepp)) {
        vec_free(ftepp->includename);
        ftepp->includename = old_includename;
        lex_close(ftepp->lex);
        ftepp->lex = old_lexer;
        return false;
    }
    vec_free(ftepp->includename);
    ftepp->includename = old_includename;
    lex_close(ftepp->lex);
    ftepp->lex = old_lexer;

    ftepp_out(ftepp, "\n#pragma file(", false);
    ftepp_out(ftepp, ctx.file, false);
    util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
    ftepp_out(ftepp, lineno, false);

    /* skip the line */
    (void)ftepp_next(ftepp);
    if (!ftepp_skipspace(ftepp))
        return false;
    if (ftepp->token != TOKEN_EOL) {
        ftepp_error(ftepp, "stray tokens after #include");
        return false;
    }
    (void)ftepp_next(ftepp);

    return true;
}

/* Basic structure handlers */
static bool ftepp_else_allowed(ftepp_t *ftepp)
{
    if (!vec_size(ftepp->conditions)) {
        ftepp_error(ftepp, "#else without #if");
        return false;
    }
    if (vec_last(ftepp->conditions).had_else) {
        ftepp_error(ftepp, "multiple #else for a single #if");
        return false;
    }
    return true;
}

static GMQCC_INLINE void ftepp_inmacro(ftepp_t *ftepp, const char *hash) {
    if (ftepp->in_macro)
        (void)!ftepp_warn(ftepp, WARN_DIRECTIVE_INMACRO, "`#%s` directive in macro", hash);
}

static bool ftepp_hash(ftepp_t *ftepp)
{
    ppcondition cond;
    ppcondition *pc;

    lex_ctx_t ctx = ftepp_ctx(ftepp);

    if (!ftepp_skipspace(ftepp))
        return false;

    switch (ftepp->token) {
        case TOKEN_KEYWORD:
        case TOKEN_IDENT:
        case TOKEN_TYPENAME:
            if (!strcmp(ftepp_tokval(ftepp), "define")) {
                ftepp_inmacro(ftepp, "define");
                return ftepp_define(ftepp);
            }
            else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
                ftepp_inmacro(ftepp, "undef");
                return ftepp_undef(ftepp);
            }
            else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
                ftepp_inmacro(ftepp, "ifdef");
                if (!ftepp_ifdef(ftepp, &cond))
                    return false;
                cond.was_on = cond.on;
                vec_push(ftepp->conditions, cond);
                ftepp->output_on = ftepp->output_on && cond.on;
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
                ftepp_inmacro(ftepp, "ifndef");
                if (!ftepp_ifdef(ftepp, &cond))
                    return false;
                cond.on = !cond.on;
                cond.was_on = cond.on;
                vec_push(ftepp->conditions, cond);
                ftepp->output_on = ftepp->output_on && cond.on;
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
                ftepp_inmacro(ftepp, "elifdef");
                if (!ftepp_else_allowed(ftepp))
                    return false;
                if (!ftepp_ifdef(ftepp, &cond))
                    return false;
                pc = &vec_last(ftepp->conditions);
                pc->on     = !pc->was_on && cond.on;
                pc->was_on = pc->was_on || pc->on;
                ftepp_update_output_condition(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
                ftepp_inmacro(ftepp, "elifndef");
                if (!ftepp_else_allowed(ftepp))
                    return false;
                if (!ftepp_ifdef(ftepp, &cond))
                    return false;
                cond.on = !cond.on;
                pc = &vec_last(ftepp->conditions);
                pc->on     = !pc->was_on && cond.on;
                pc->was_on = pc->was_on || pc->on;
                ftepp_update_output_condition(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
                ftepp_inmacro(ftepp, "elif");
                if (!ftepp_else_allowed(ftepp))
                    return false;
                if (!ftepp_if(ftepp, &cond))
                    return false;
                pc = &vec_last(ftepp->conditions);
                pc->on     = !pc->was_on && cond.on;
                pc->was_on = pc->was_on  || pc->on;
                ftepp_update_output_condition(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "if")) {
                ftepp_inmacro(ftepp, "if");
                if (!ftepp_if(ftepp, &cond))
                    return false;
                cond.was_on = cond.on;
                vec_push(ftepp->conditions, cond);
                ftepp->output_on = ftepp->output_on && cond.on;
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "else")) {
                ftepp_inmacro(ftepp, "else");
                if (!ftepp_else_allowed(ftepp))
                    return false;
                pc = &vec_last(ftepp->conditions);
                pc->on = !pc->was_on;
                pc->had_else = true;
                ftepp_next(ftepp);
                ftepp_update_output_condition(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
                ftepp_inmacro(ftepp, "endif");
                if (!vec_size(ftepp->conditions)) {
                    ftepp_error(ftepp, "#endif without #if");
                    return false;
                }
                vec_pop(ftepp->conditions);
                ftepp_next(ftepp);
                ftepp_update_output_condition(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "include")) {
                ftepp_inmacro(ftepp, "include");
                return ftepp_include(ftepp);
            }
            else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
                ftepp_out(ftepp, "#", false);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
                ftepp_directive_warning(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "error")) {
                ftepp_directive_error(ftepp);
                break;
            }
            else if (!strcmp(ftepp_tokval(ftepp), "message")) {
                ftepp_directive_message(ftepp);
                break;
            }
            else {
                if (ftepp->output_on) {
                    ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
                    return false;
                } else {
                    ftepp_next(ftepp);
                    break;
                }
            }
            /* break; never reached */
        default:
            ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
            return false;
        case TOKEN_EOL:
            ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
            return false;
        case TOKEN_EOF:
            ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
            return false;

        /* Builtins! Don't forget the builtins! */
        case TOKEN_INTCONST:
        case TOKEN_FLOATCONST:
            ftepp_out(ftepp, "#", false);
            return true;
    }
    if (!ftepp_skipspace(ftepp))
        return false;
    return true;
}

static bool ftepp_preprocess(ftepp_t *ftepp)
{
    ppmacro *macro;
    bool     newline = true;

    /* predef stuff */
    char    *expand  = NULL;

    ftepp->lex->flags.preprocessing = true;
    ftepp->lex->flags.mergelines    = false;
    ftepp->lex->flags.noops         = true;

    ftepp_next(ftepp);
    do
    {
        if (ftepp->token >= TOKEN_EOF)
            break;
#if 0
        newline = true;
#endif

        switch (ftepp->token) {
            case TOKEN_KEYWORD:
            case TOKEN_IDENT:
            case TOKEN_TYPENAME:
                /* is it a predef? */
                if (OPTS_FLAG(FTEPP_PREDEFS)) {
                    char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
                    if (predef) {
                        expand = predef(ftepp);
                        ftepp_out (ftepp, expand, false);
                        ftepp_next(ftepp);

                        mem_d(expand);
                        break;
                    }
                }

                if (ftepp->output_on)
                    macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
                else
                    macro = NULL;

                if (!macro) {
                    ftepp_out(ftepp, ftepp_tokval(ftepp), false);
                    ftepp_next(ftepp);
                    break;
                }
                if (!ftepp_macro_call(ftepp, macro))
                    ftepp->token = TOKEN_ERROR;
                break;
            case '#':
                if (!newline) {
                    ftepp_out(ftepp, ftepp_tokval(ftepp), false);
                    ftepp_next(ftepp);
                    break;
                }
                ftepp->lex->flags.mergelines = true;
                if (ftepp_next(ftepp) >= TOKEN_EOF) {
                    ftepp_error(ftepp, "error in preprocessor directive");
                    ftepp->token = TOKEN_ERROR;
                    break;
                }
                if (!ftepp_hash(ftepp))
                    ftepp->token = TOKEN_ERROR;
                ftepp->lex->flags.mergelines = false;
                break;
            case TOKEN_EOL:
                newline = true;
                ftepp_out(ftepp, "\n", true);
                ftepp_next(ftepp);
                break;
            case TOKEN_WHITE:
                /* same as default but don't set newline=false */
                ftepp_out(ftepp, ftepp_tokval(ftepp), true);
                ftepp_next(ftepp);
                break;
            default:
                newline = false;
                ftepp_out(ftepp, ftepp_tokval(ftepp), false);
                ftepp_next(ftepp);
                break;
        }
    } while (!ftepp->errors && ftepp->token < TOKEN_EOF);

    /* force a 0 at the end but don't count it as added to the output */
    vec_push(ftepp->output_string, 0);
    vec_shrinkby(ftepp->output_string, 1);

    return (ftepp->token == TOKEN_EOF);
}

/* Like in parser.c - files keep the previous state so we have one global
 * preprocessor. Except here we will want to warn about dangling #ifs.
 */
static bool ftepp_preprocess_done(ftepp_t *ftepp)
{
    bool retval = true;
    if (vec_size(ftepp->conditions)) {
        if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
            retval = false;
    }
    lex_close(ftepp->lex);
    ftepp->lex = NULL;
    if (ftepp->itemname) {
        mem_d(ftepp->itemname);
        ftepp->itemname = NULL;
    }
    return retval;
}

bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
{
    ftepp->lex = lex_open(filename);
    ftepp->itemname = util_strdup(filename);
    if (!ftepp->lex) {
        con_out("failed to open file \"%s\"\n", filename);
        return false;
    }
    if (!ftepp_preprocess(ftepp))
        return false;
    return ftepp_preprocess_done(ftepp);
}

bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
{
    ftepp->lex = lex_open_string(str, strlen(str), name);
    ftepp->itemname = util_strdup(name);
    if (!ftepp->lex) {
        con_out("failed to create lexer for string \"%s\"\n", name);
        return false;
    }
    if (!ftepp_preprocess(ftepp))
        return false;
    return ftepp_preprocess_done(ftepp);
}

void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
    char *create = NULL;

    /* use saner path for empty macros */
    if (!value) {
        ftepp_add_define(ftepp, "__builtin__", name);
        return;
    }

    vec_append(create, 8,           "#define ");
    vec_append(create, strlen(name), name);
    vec_push  (create, ' ');
    vec_append(create, strlen(value), value);
    vec_push  (create, 0);

    ftepp_preprocess_string(ftepp, "__builtin__", create);
    vec_free  (create);
}

ftepp_t *ftepp_create()
{
    ftepp_t *ftepp;
    char minor[32];
    char major[32];
    size_t i;

    ftepp = ftepp_new();
    if (!ftepp)
        return NULL;

    memset(minor, 0, sizeof(minor));
    memset(major, 0, sizeof(major));

    /* set the right macro based on the selected standard */
    ftepp_add_define(ftepp, NULL, "GMQCC");
    if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
        ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
        /* 1.00 */
        major[0] = '"';
        major[1] = '1';
        major[2] = '"';

        minor[0] = '"';
        minor[1] = '0';
        minor[2] = '"';
    } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
        ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
        util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
        util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
    } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
        ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
        util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
        util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
    } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
        ftepp_add_define(ftepp, NULL, "__STD_QCC__");
        /* 1.0 */
        major[0] = '"';
        major[1] = '1';
        major[2] = '"';

        minor[0] = '"';
        minor[1] = '0';
        minor[2] = '"';
    }

    ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
    ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);

    /*
     * We're going to just make __NULL__ nil, which works for 60% of the
     * cases of __NULL_ for fteqcc.
     */
    ftepp_add_macro(ftepp, "__NULL__", "nil");

    /* add all the math constants if they can be */
    if (OPTS_FLAG(FTEPP_MATHDEFS)) {
        for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++)
            if (!ftepp_macro_find(ftepp, ftepp_math_constants[i][0]))
                ftepp_add_macro(ftepp, ftepp_math_constants[i][0], ftepp_math_constants[i][1]);
    }

    return ftepp;
}

void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
{
    ppmacro *macro;
    lex_ctx_t ctx = { "__builtin__", 0, 0 };
    ctx.file = source;
    macro = ppmacro_new(ctx, name);
    /*vec_push(ftepp->macros, macro);*/
    util_htset(ftepp->macros, name, macro);
}

const char *ftepp_get(ftepp_t *ftepp)
{
    return ftepp->output_string;
}

void ftepp_flush(ftepp_t *ftepp)
{
    ftepp_flush_do(ftepp);
}

void ftepp_finish(ftepp_t *ftepp)
{
    if (!ftepp)
        return;
    ftepp_delete(ftepp);
}


text:
package main

import (
	"flag"
	"fmt"
	"log"
	"strings"
	"time"
)

const (
	// TODO(mpl): allow for awake and asleep as parameters, as long as awake + asleep == 28
	h24            = 24 * time.Hour
	h28            = 28 * time.Hour
	awake          = 19 * time.Hour
	asleep         = 9 * time.Hour
	firstDayOfWeek = monday
	saneKitchen    = "15:04"
)

var (
	dayFrom = flag.String("day", "monday", "day to build the week from")
	wake    = flag.String("wake", "08:00", "time to wake up on the day to build the week from")
)

const (
	monday int = iota
	tuesday
	wednesday
	thursday
	friday
	saturday
	sunday
)

var toString = map[int]string{
	monday:    "monday",
	tuesday:   "tuesday",
	wednesday: "wednesday",
	thursday:  "thursday",
	friday:    "friday",
	saturday:  "saturday",
	sunday:    "sunday",
}

func main() {
	flag.Parse()
	wakeTime, err := time.Parse(saneKitchen, *wake)
	if err != nil {
		log.Fatal(err)
	}
	// TODO(mpl): ask about origin != time.Time{}
	origin, err := time.Parse(saneKitchen, "00:00")
	if err != nil {
		log.Fatal(err)
	}
	initDay, err := initialDay(wakeTime, origin)
	if err != nil {
		log.Fatal(err)
	}
	wk := week(initDay, origin)
	wk = shift(wk, firstDayOfWeek)

	for _, v := range wk {
		fmt.Printf("%v	%v\n", toString[v.name][:3], v.String())
	}

}

type weekDay struct {
	name   int
	wakeup time.Time
	bed    time.Time
}

func (w weekDay) String() string {
	if w.wakeup.IsZero() {
		return fmt.Sprintf("up %v bed", w.bed.Format(saneKitchen))
	}
	if w.bed.IsZero() {
		return fmt.Sprintf("bed %v up", w.wakeup.Format(saneKitchen))
	}
	if w.wakeup.Before(w.bed) {
		return fmt.Sprintf("bed %v up %v bed", w.wakeup.Format(saneKitchen), w.bed.Format(saneKitchen))
	}
	return fmt.Sprintf("up %v bed %v up", w.bed.Format(saneKitchen), w.wakeup.Format(saneKitchen))
}

func initialDay(wakeTime, origin time.Time) (weekDay, error) {
	var initial weekDay
	fromString := func(daystring string) int {
		for k, v := range toString {
			if strings.EqualFold(v, daystring) {
				return k
			}
		}
		return -1
	}
	name := fromString(*dayFrom)
	if name == -1 {
		return initial, fmt.Errorf("Invalid day name: %v", *dayFrom)
	}
	yesterday := origin
	if wakeTime.Before(yesterday) {
		return initial, fmt.Errorf("wakeTime is in the past")
	}
	if wakeTime.Equal(yesterday) {
		bedTime := wakeTime.Add(awake)
		wakeTime = bedTime.Add(asleep)
		return weekDay{
			name:   name,
			wakeup: wakeTime,
			bed:    bedTime,
		}, nil
	}
	bedTime := wakeTime.Add(-asleep)
	if bedTime.Before(yesterday) || bedTime.Equal(yesterday) {
		bedTime = wakeTime.Add(awake)
	}
	initial = weekDay{
		name:   name,
		wakeup: wakeTime,
		bed:    bedTime,
	}
	return initial, nil
}

func week(initialDay weekDay, origin time.Time) []weekDay {
	yesterday := origin
	tomorrow := yesterday.Add(h24)
	wk := make([]weekDay, 7)
	wakeup := initialDay.wakeup
	bed := initialDay.bed
	name := initialDay.name
	wakes := []time.Time{wakeup}
	beds := []time.Time{bed}
	for i := 0; i < 7; i++ {
		wakeup = wakeup.Add(h28)
		bed = bed.Add(h28)
		wakes = append(wakes, wakeup)
		beds = append(beds, bed)
	}
	iw, ib := 0, 0
	for k, _ := range wk {
		newDay := weekDay{
			name: name,
		}
		if wakes[iw].After(yesterday) && (wakes[iw].Before(tomorrow) || wakes[iw].Equal(tomorrow)) {
			newDay.wakeup = wakes[iw]
			iw++
		}
		if beds[ib].After(yesterday) && (beds[ib].Before(tomorrow) || beds[ib].Equal(tomorrow)) {
			newDay.bed = beds[ib]
			ib++
		}
		wk[k] = newDay
		name++
		if name == 7 {
			name = 0
		}
		yesterday = yesterday.Add(h24)
		tomorrow = tomorrow.Add(h24)
	}
	return wk
}

func shift(week []weekDay, asFirst int) []weekDay {
	firstPos := 7 - week[0].name + asFirst
	return append(week[firstPos:], week[:firstPos]...)
}