<commit_before>// Requires Adafruit_ASFcore library!

// Be careful to use a platform-specific conditional include to only make the
// code visible for the appropriate platform.  Arduino will try to compile and
// link all .cpp files regardless of platform.
#if defined(ARDUINO_ARCH_SAMD)

#include <sam.h>
#include "WatchdogSAMD.h"

int WatchdogSAMD::enable(int maxPeriodMS, bool isForSleep) {
    // Enable the watchdog with a period up to the specified max period in
    // milliseconds.

    // Review the watchdog section from the SAMD21 datasheet section 17:
    // http://www.atmel.com/images/atmel-42181-sam-d21_datasheet.pdf

    int     cycles;
    uint8_t bits;

    if(!_initialized) _initialize_wdt();

#if defined(__SAMD51__)
    WDT->CTRLA.reg = 0; // Disable watchdog for config
    while(WDT->SYNCBUSY.reg);
#else
    WDT->CTRL.reg = 0; // Disable watchdog for config
    while(WDT->STATUS.bit.SYNCBUSY);
#endif

    // You'll see some occasional conversion here compensating between
    // milliseconds (1000 Hz) and WDT clock cycles (~1024 Hz).  The low-
    // power oscillator used by the WDT ostensibly runs at 32,768 Hz with
    // a 1:32 prescale, thus 1024 Hz, though probably not super precise.

    if((maxPeriodMS >= 16000) || !maxPeriodMS) {
        cycles = 16384;
        bits   = 0xB;
    } else {
        cycles = (maxPeriodMS * 1024L + 500) / 1000; // ms -> WDT cycles
        if(cycles >= 8192) {
            cycles = 8192;
            bits   = 0xA;
        } else if(cycles >= 4096) {
            cycles = 4096;
            bits   = 0x9;
        } else if(cycles >= 2048) {
            cycles = 2048;
            bits   = 0x8;
        } else if(cycles >= 1024) {
            cycles = 1024;
            bits   = 0x7;
        } else if(cycles >= 512) {
            cycles = 512;
            bits   = 0x6;
        } else if(cycles >= 256) {
            cycles = 256;
            bits   = 0x5;
        } else if(cycles >= 128) {
            cycles = 128;
            bits   = 0x4;
        } else if(cycles >= 64) {
            cycles = 64;
            bits   = 0x3;
        } else if(cycles >= 32) {
            cycles = 32;
            bits   = 0x2;
        } else if(cycles >= 16) {
            cycles = 16;
            bits   = 0x1;
        } else {
            cycles = 8;
            bits   = 0x0;
        }
    }

    // Watchdog timer on SAMD is a slightly different animal than on AVR.
    // On AVR, the WTD timeout is configured in one register and then an
    // interrupt can optionally be enabled to handle the timeout in code
    // (as in waking from sleep) vs resetting the chip.  Easy.
    // On SAMD, when the WDT fires, that's it, the chip's getting reset.
    // Instead, it has an "early warning interrupt" with a different set
    // interval prior to the reset.  For equivalent behavior to the AVR
    // library, this requires a slightly different configuration depending
    // whether we're coming from the sleep() function (which needs the
    // interrupt), or just enable() (no interrupt, we want the chip reset
    // unless the WDT is cleared first).  In the sleep case, 'windowed'
    // mode is used in order to allow access to the longest available
    // sleep interval (about 16 sec); the WDT 'period' (when a reset
    // occurs) follows this and is always just set to the max, since the
    // interrupt will trigger first.  In the enable case, windowed mode
    // is not used, the WDT period is set and that's that.
    // The 'isForSleep' argument determines which behavior is used;
    // this isn't present in the AVR code, just here.  It defaults to
    // 'false' so existing Arduino code works as normal, while the sleep()
    // function (later in this file) explicitly passes 'true' to get the
    // alternate behavior.

#if defined(__SAMD51__)
    if(isForSleep) {
        WDT->INTFLAG.bit.EW      = 1;    // Clear interrupt flag
        WDT->INTENSET.bit.EW     = 1;    // Enable early warning interrupt
        WDT->CONFIG.bit.PER      = 0xB;  // Period = max
        WDT->CONFIG.bit.WINDOW   = bits; // Set time of interrupt
        WDT->EWCTRL.bit.EWOFFSET = 0x0;  // Early warning offset
        WDT->CTRLA.bit.WEN       = 1;    // Enable window mode
        while(WDT->SYNCBUSY.reg);        // Sync CTRL write
    } else {
        WDT->INTENCLR.bit.EW     = 1;    // Disable early warning interrupt
        WDT->CONFIG.bit.PER      = bits; // Set period for chip reset
        WDT->CTRLA.bit.WEN       = 0;    // Disable window mode
        while(WDT->SYNCBUSY.reg);        // Sync CTRL write
    }

    reset();                             // Clear watchdog interval
    WDT->CTRLA.bit.ENABLE = 1;           // Start watchdog now!
    while(WDT->SYNCBUSY.reg);
#else
    if(isForSleep) {
        WDT->INTENSET.bit.EW   = 1;      // Enable early warning interrupt
        WDT->CONFIG.bit.PER    = 0xB;    // Period = max
        WDT->CONFIG.bit.WINDOW = bits;   // Set time of interrupt
        WDT->CTRL.bit.WEN      = 1;      // Enable window mode
        while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
    } else {
        WDT->INTENCLR.bit.EW   = 1;      // Disable early warning interrupt
        WDT->CONFIG.bit.PER    = bits;   // Set period for chip reset
        WDT->CTRL.bit.WEN      = 0;      // Disable window mode
        while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
    }

    reset();                             // Clear watchdog interval
    WDT->CTRL.bit.ENABLE = 1;            // Start watchdog now!
    while(WDT->STATUS.bit.SYNCBUSY);
#endif

    return (cycles * 1000L + 512) / 1024; // WDT cycles -> ms
}

void WatchdogSAMD::reset() {
    // Write the watchdog clear key value (0xA5) to the watchdog
    // clear register to clear the watchdog timer and reset it.
#if defined(__SAMD51__)
    while(WDT->SYNCBUSY.reg);
#else
    while(WDT->STATUS.bit.SYNCBUSY);
#endif
    WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
}

uint8_t WatchdogSAMD::resetCause() {
  return RSTC->RCAUSE.reg;
}

void WatchdogSAMD::disable() {
#if defined(__SAMD51__)
    WDT->CTRLA.bit.ENABLE = 0;
    while(WDT->SYNCBUSY.reg);
#else
    WDT->CTRL.bit.ENABLE = 0;
    while(WDT->STATUS.bit.SYNCBUSY);
#endif
}

void WDT_Handler(void) {
    // ISR for watchdog early warning, DO NOT RENAME!
#if defined(__SAMD51__)
    WDT->CTRLA.bit.ENABLE = 0;       // Disable watchdog
    while(WDT->SYNCBUSY.reg);
#else
    WDT->CTRL.bit.ENABLE = 0;        // Disable watchdog
    while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
#endif
    WDT->INTFLAG.bit.EW  = 1;        // Clear interrupt flag
}

int WatchdogSAMD::sleep(int maxPeriodMS) {

    int actualPeriodMS = enable(maxPeriodMS, true); // true = for sleep

    // Enable standby sleep mode (deepest sleep) and activate.
    // Insights from Atmel ASF library.
#if (SAMD20 || SAMD21)
    // Don't fully power down flash when in sleep
    NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val;
#endif
#if defined(__SAMD51__)
    PM->SLEEPCFG.bit.SLEEPMODE = 0x4;         // Standby sleep mode
    while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4); // Wait for it to take
#else
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
#endif

    __DSB(); // Data sync to ensure outgoing memory accesses complete
    __WFI(); // Wait for interrupt (places device in sleep mode)

    // Code resumes here on wake (WDT early warning interrupt).
    // Bug: the return value assumes the WDT has run its course;
    // incorrect if the device woke due to an external interrupt.
    // Without an external RTC there's no way to provide a correct
    // sleep period in the latter case...but at the very least,
    // might indicate said condition occurred by returning 0 instead
    // (assuming we can pin down which interrupt caused the wake).

    return actualPeriodMS;
}

void WatchdogSAMD::_initialize_wdt() {
    // One-time initialization of watchdog timer.
    // Insights from rickrlh and rbrucemtl in Arduino forum!

#if defined(__SAMD51__)
    // SAMD51 WDT uses OSCULP32k as input clock now
    // section: 20.5.3
    OSC32KCTRL->OSCULP32K.bit.EN1K  = 1; // Enable out 1K (for WDT)
    OSC32KCTRL->OSCULP32K.bit.EN32K = 0; // Disable out 32K

    // Enable WDT early-warning interrupt
    NVIC_DisableIRQ(WDT_IRQn);
    NVIC_ClearPendingIRQ(WDT_IRQn);
    NVIC_SetPriority(WDT_IRQn, 0); // Top priority
    NVIC_EnableIRQ(WDT_IRQn);

    while(WDT->SYNCBUSY.reg);

    USB->DEVICE.CTRLA.bit.ENABLE = 0;         // Disable the USB peripheral
    while(USB->DEVICE.SYNCBUSY.bit.ENABLE);   // Wait for synchronization
    USB->DEVICE.CTRLA.bit.RUNSTDBY = 0;       // Deactivate run on standby
    USB->DEVICE.CTRLA.bit.ENABLE = 1;         // Enable the USB peripheral
    while(USB->DEVICE.SYNCBUSY.bit.ENABLE);   // Wait for synchronization
#else
    // Generic clock generator 2, divisor = 32 (2^(DIV+1))
    GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
    // Enable clock generator 2 using low-power 32KHz oscillator.
    // With /32 divisor above, this yields 1024Hz(ish) clock.
    GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
                        GCLK_GENCTRL_GENEN |
                        GCLK_GENCTRL_SRC_OSCULP32K |
                        GCLK_GENCTRL_DIVSEL;
    while(GCLK->STATUS.bit.SYNCBUSY);
    // WDT clock = clock gen 2
    GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
                        GCLK_CLKCTRL_CLKEN |
                        GCLK_CLKCTRL_GEN_GCLK2;

    // Enable WDT early-warning interrupt
    NVIC_DisableIRQ(WDT_IRQn);
    NVIC_ClearPendingIRQ(WDT_IRQn);
    NVIC_SetPriority(WDT_IRQn, 0); // Top priority
    NVIC_EnableIRQ(WDT_IRQn);
#endif

    _initialized = true;
}

#endif // defined(ARDUINO_ARCH_SAMD)
<commit_msg>fix samd21<commit_after>// Requires Adafruit_ASFcore library!

// Be careful to use a platform-specific conditional include to only make the
// code visible for the appropriate platform.  Arduino will try to compile and
// link all .cpp files regardless of platform.
#if defined(ARDUINO_ARCH_SAMD)

#include <sam.h>
#include "WatchdogSAMD.h"

int WatchdogSAMD::enable(int maxPeriodMS, bool isForSleep) {
    // Enable the watchdog with a period up to the specified max period in
    // milliseconds.

    // Review the watchdog section from the SAMD21 datasheet section 17:
    // http://www.atmel.com/images/atmel-42181-sam-d21_datasheet.pdf

    int     cycles;
    uint8_t bits;

    if(!_initialized) _initialize_wdt();

#if defined(__SAMD51__)
    WDT->CTRLA.reg = 0; // Disable watchdog for config
    while(WDT->SYNCBUSY.reg);
#else
    WDT->CTRL.reg = 0; // Disable watchdog for config
    while(WDT->STATUS.bit.SYNCBUSY);
#endif

    // You'll see some occasional conversion here compensating between
    // milliseconds (1000 Hz) and WDT clock cycles (~1024 Hz).  The low-
    // power oscillator used by the WDT ostensibly runs at 32,768 Hz with
    // a 1:32 prescale, thus 1024 Hz, though probably not super precise.

    if((maxPeriodMS >= 16000) || !maxPeriodMS) {
        cycles = 16384;
        bits   = 0xB;
    } else {
        cycles = (maxPeriodMS * 1024L + 500) / 1000; // ms -> WDT cycles
        if(cycles >= 8192) {
            cycles = 8192;
            bits   = 0xA;
        } else if(cycles >= 4096) {
            cycles = 4096;
            bits   = 0x9;
        } else if(cycles >= 2048) {
            cycles = 2048;
            bits   = 0x8;
        } else if(cycles >= 1024) {
            cycles = 1024;
            bits   = 0x7;
        } else if(cycles >= 512) {
            cycles = 512;
            bits   = 0x6;
        } else if(cycles >= 256) {
            cycles = 256;
            bits   = 0x5;
        } else if(cycles >= 128) {
            cycles = 128;
            bits   = 0x4;
        } else if(cycles >= 64) {
            cycles = 64;
            bits   = 0x3;
        } else if(cycles >= 32) {
            cycles = 32;
            bits   = 0x2;
        } else if(cycles >= 16) {
            cycles = 16;
            bits   = 0x1;
        } else {
            cycles = 8;
            bits   = 0x0;
        }
    }

    // Watchdog timer on SAMD is a slightly different animal than on AVR.
    // On AVR, the WTD timeout is configured in one register and then an
    // interrupt can optionally be enabled to handle the timeout in code
    // (as in waking from sleep) vs resetting the chip.  Easy.
    // On SAMD, when the WDT fires, that's it, the chip's getting reset.
    // Instead, it has an "early warning interrupt" with a different set
    // interval prior to the reset.  For equivalent behavior to the AVR
    // library, this requires a slightly different configuration depending
    // whether we're coming from the sleep() function (which needs the
    // interrupt), or just enable() (no interrupt, we want the chip reset
    // unless the WDT is cleared first).  In the sleep case, 'windowed'
    // mode is used in order to allow access to the longest available
    // sleep interval (about 16 sec); the WDT 'period' (when a reset
    // occurs) follows this and is always just set to the max, since the
    // interrupt will trigger first.  In the enable case, windowed mode
    // is not used, the WDT period is set and that's that.
    // The 'isForSleep' argument determines which behavior is used;
    // this isn't present in the AVR code, just here.  It defaults to
    // 'false' so existing Arduino code works as normal, while the sleep()
    // function (later in this file) explicitly passes 'true' to get the
    // alternate behavior.

#if defined(__SAMD51__)
    if(isForSleep) {
        WDT->INTFLAG.bit.EW      = 1;    // Clear interrupt flag
        WDT->INTENSET.bit.EW     = 1;    // Enable early warning interrupt
        WDT->CONFIG.bit.PER      = 0xB;  // Period = max
        WDT->CONFIG.bit.WINDOW   = bits; // Set time of interrupt
        WDT->EWCTRL.bit.EWOFFSET = 0x0;  // Early warning offset
        WDT->CTRLA.bit.WEN       = 1;    // Enable window mode
        while(WDT->SYNCBUSY.reg);        // Sync CTRL write
    } else {
        WDT->INTENCLR.bit.EW     = 1;    // Disable early warning interrupt
        WDT->CONFIG.bit.PER      = bits; // Set period for chip reset
        WDT->CTRLA.bit.WEN       = 0;    // Disable window mode
        while(WDT->SYNCBUSY.reg);        // Sync CTRL write
    }

    reset();                             // Clear watchdog interval
    WDT->CTRLA.bit.ENABLE = 1;           // Start watchdog now!
    while(WDT->SYNCBUSY.reg);
#else
    if(isForSleep) {
        WDT->INTENSET.bit.EW   = 1;      // Enable early warning interrupt
        WDT->CONFIG.bit.PER    = 0xB;    // Period = max
        WDT->CONFIG.bit.WINDOW = bits;   // Set time of interrupt
        WDT->CTRL.bit.WEN      = 1;      // Enable window mode
        while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
    } else {
        WDT->INTENCLR.bit.EW   = 1;      // Disable early warning interrupt
        WDT->CONFIG.bit.PER    = bits;   // Set period for chip reset
        WDT->CTRL.bit.WEN      = 0;      // Disable window mode
        while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
    }

    reset();                             // Clear watchdog interval
    WDT->CTRL.bit.ENABLE = 1;            // Start watchdog now!
    while(WDT->STATUS.bit.SYNCBUSY);
#endif

    return (cycles * 1000L + 512) / 1024; // WDT cycles -> ms
}

void WatchdogSAMD::reset() {
    // Write the watchdog clear key value (0xA5) to the watchdog
    // clear register to clear the watchdog timer and reset it.
#if defined(__SAMD51__)
    while(WDT->SYNCBUSY.reg);
#else
    while(WDT->STATUS.bit.SYNCBUSY);
#endif
    WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
}

uint8_t WatchdogSAMD::resetCause() {
#if defined(__SAMD51__)
  return RSTC->RCAUSE.reg;
#else
  return PM->RCAUSE.reg;
#endif
}

void WatchdogSAMD::disable() {
#if defined(__SAMD51__)
    WDT->CTRLA.bit.ENABLE = 0;
    while(WDT->SYNCBUSY.reg);
#else
    WDT->CTRL.bit.ENABLE = 0;
    while(WDT->STATUS.bit.SYNCBUSY);
#endif
}

void WDT_Handler(void) {
    // ISR for watchdog early warning, DO NOT RENAME!
#if defined(__SAMD51__)
    WDT->CTRLA.bit.ENABLE = 0;       // Disable watchdog
    while(WDT->SYNCBUSY.reg);
#else
    WDT->CTRL.bit.ENABLE = 0;        // Disable watchdog
    while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
#endif
    WDT->INTFLAG.bit.EW  = 1;        // Clear interrupt flag
}

int WatchdogSAMD::sleep(int maxPeriodMS) {

    int actualPeriodMS = enable(maxPeriodMS, true); // true = for sleep

    // Enable standby sleep mode (deepest sleep) and activate.
    // Insights from Atmel ASF library.
#if (SAMD20 || SAMD21)
    // Don't fully power down flash when in sleep
    NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val;
#endif
#if defined(__SAMD51__)
    PM->SLEEPCFG.bit.SLEEPMODE = 0x4;         // Standby sleep mode
    while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4); // Wait for it to take
#else
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
#endif

    __DSB(); // Data sync to ensure outgoing memory accesses complete
    __WFI(); // Wait for interrupt (places device in sleep mode)

    // Code resumes here on wake (WDT early warning interrupt).
    // Bug: the return value assumes the WDT has run its course;
    // incorrect if the device woke due to an external interrupt.
    // Without an external RTC there's no way to provide a correct
    // sleep period in the latter case...but at the very least,
    // might indicate said condition occurred by returning 0 instead
    // (assuming we can pin down which interrupt caused the wake).

    return actualPeriodMS;
}

void WatchdogSAMD::_initialize_wdt() {
    // One-time initialization of watchdog timer.
    // Insights from rickrlh and rbrucemtl in Arduino forum!

#if defined(__SAMD51__)
    // SAMD51 WDT uses OSCULP32k as input clock now
    // section: 20.5.3
    OSC32KCTRL->OSCULP32K.bit.EN1K  = 1; // Enable out 1K (for WDT)
    OSC32KCTRL->OSCULP32K.bit.EN32K = 0; // Disable out 32K

    // Enable WDT early-warning interrupt
    NVIC_DisableIRQ(WDT_IRQn);
    NVIC_ClearPendingIRQ(WDT_IRQn);
    NVIC_SetPriority(WDT_IRQn, 0); // Top priority
    NVIC_EnableIRQ(WDT_IRQn);

    while(WDT->SYNCBUSY.reg);

    USB->DEVICE.CTRLA.bit.ENABLE = 0;         // Disable the USB peripheral
    while(USB->DEVICE.SYNCBUSY.bit.ENABLE);   // Wait for synchronization
    USB->DEVICE.CTRLA.bit.RUNSTDBY = 0;       // Deactivate run on standby
    USB->DEVICE.CTRLA.bit.ENABLE = 1;         // Enable the USB peripheral
    while(USB->DEVICE.SYNCBUSY.bit.ENABLE);   // Wait for synchronization
#else
    // Generic clock generator 2, divisor = 32 (2^(DIV+1))
    GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
    // Enable clock generator 2 using low-power 32KHz oscillator.
    // With /32 divisor above, this yields 1024Hz(ish) clock.
    GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
                        GCLK_GENCTRL_GENEN |
                        GCLK_GENCTRL_SRC_OSCULP32K |
                        GCLK_GENCTRL_DIVSEL;
    while(GCLK->STATUS.bit.SYNCBUSY);
    // WDT clock = clock gen 2
    GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
                        GCLK_CLKCTRL_CLKEN |
                        GCLK_CLKCTRL_GEN_GCLK2;

    // Enable WDT early-warning interrupt
    NVIC_DisableIRQ(WDT_IRQn);
    NVIC_ClearPendingIRQ(WDT_IRQn);
    NVIC_SetPriority(WDT_IRQn, 0); // Top priority
    NVIC_EnableIRQ(WDT_IRQn);
#endif

    _initialized = true;
}

#endif // defined(ARDUINO_ARCH_SAMD)
<|endoftext|>


<commit_before>
#include <atomic>
#include <thread>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <mutex>
#include <condition_variable>
#include <chrono>

#include "stress_instance.h"

static constexpr std::size_t max_threads = 256;

typedef std::chrono::system_clock Clock;
typedef std::chrono::time_point<Clock> TimePoint;
typedef std::chrono::duration<double> Duration;

static void error(char const* message, int exitcode = 2)
{
    std::cerr << message << std::endl;
    exit(exitcode);
}

static int run_stress(int cpucount, int timeLimit)
{
    // Sidestep all the issues with copying
    // and ensure flat memory layout
    std::unique_ptr<stress_instance[]> instances(new stress_instance[cpucount]);
    stress_instance* begin = &instances[0];
    stress_instance* end = &instances[cpucount];
    std::for_each(begin, end,
    [](stress_instance& instance)
    {
        instance.run();
    });

    // Use a condition variable wait for delays
    // It looks more like we are doing work than
    // it would if we were calling sleep_for
    std::mutex dummyMutex;
    std::condition_variable dummyConditionVariable;
    std::unique_lock<std::mutex> lock(dummyMutex);
    do
    {
        dummyConditionVariable.wait_until(lock, Clock::now() + Duration(1));

        std::for_each(begin, end,
        [](stress_instance& instance)
        {
            std::cout << std::setw(5) <<
                         (instance.getCount() / 1000000) <<
                         "M ";
        });
        std::cout << std::endl;

        // Only decrement it if it is positive
        timeLimit -= (timeLimit > 0);
    }
    while (timeLimit != 0);
}

int main(int argc, char** argv)
{
    int threads = 0;
    bool force = false;
    int timeLimit = -1;

    for (int i = 1; i < argc; ++i)
    {
        if (!strcmp(argv[i], "-t"))
        {
            if (i + 1 >= argc)
                error("Missing argument to thread count option (-t)");
            threads = atoi(argv[++i]);
        }
        else if (!strcmp(argv[i], "-i"))
        {
            if (i + 1 >= argc)
                error("Missing argument to iteration count option (-i)");
            timeLimit = atoi(argv[++i]);
        }
        else if (!strcmp(argv[i], "-f"))
        {
            force = true;
        }
        else
        {
            error((std::string("Invalid switch: ") + argv[i]).c_str());
        }
    }

    std::size_t cpuCount = std::thread::hardware_concurrency();

    if (threads <= 0)
    {
        std::cerr << "Using all " << cpuCount << " CPUs" << std::endl;
        threads = cpuCount;
    }

    if (threads > cpuCount)
    {
        if (force)
        {
            std::cerr << "Warning, forced to use more threads"
                         " than CPUs, " << threads << " threads,"
                         " but only " << cpuCount <<
                         " CPUs detected!" << std::endl;
        }
        else
        {
            std::cerr << "Capping to " << cpuCount <<
                    ". Use -f to override." << std::endl;
            threads = cpuCount;
        }
    }

    if (threads > max_threads)
    {
        std::cerr << "Capping to " << max_threads <<
                ". Use -f to override." << std::endl;
        threads = max_threads;
    }

    return run_stress(threads, timeLimit);
}
<commit_msg>Improved command line interface<commit_after>
#include <atomic>
#include <thread>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <mutex>
#include <condition_variable>
#include <chrono>

#include "stress_instance.h"

// This is only to limit the number of threads to a sane number
static constexpr std::size_t max_threads = 256;

typedef std::chrono::system_clock Clock;
typedef std::chrono::time_point<Clock> TimePoint;
typedef std::chrono::duration<double> Duration;

static void error(char const* message, int exitcode = 2)
{
    std::cerr << message << std::endl;
    exit(exitcode);
}

static int run_stress(int cpucount, int timeLimit)
{
    // Sidestep all the issues with copying
    // and ensure flat memory layout
    std::unique_ptr<stress_instance[]> instances(new stress_instance[cpucount]);
    stress_instance* begin = &instances[0];
    stress_instance* end = &instances[cpucount];
    std::for_each(begin, end,
    [](stress_instance& instance)
    {
        instance.run();
    });

    // Use a condition variable wait for delays
    // It looks more like we are doing work than
    // it would if we were calling sleep_for
    std::mutex dummyMutex;
    std::condition_variable dummyConditionVariable;
    std::unique_lock<std::mutex> lock(dummyMutex);
    do
    {
        dummyConditionVariable.wait_until(lock, Clock::now() + Duration(1));

        std::for_each(begin, end,
        [](stress_instance& instance)
        {
            std::cout << std::setw(5) <<
                         (instance.getCount() / 1000000) <<
                         "M ";
        });
        std::cout << std::endl;

        // Only decrement it if it is positive
        timeLimit -= (timeLimit > 0);
    }
    while (timeLimit != 0);
}

int main(int argc, char** argv)
{
    int threads = 0;
    bool force = false;
    int timeLimit = -1;

    for (int i = 1; i < argc; ++i)
    {
        if (!strcmp(argv[i], "-t"))
        {
            if (i + 1 >= argc)
                error("Missing argument to thread count option (-t)");
            threads = atoi(argv[++i]);
        }
        else if (!strcmp(argv[i], "-i"))
        {
            if (i + 1 >= argc)
                error("Missing argument to iteration count option (-i)");
            timeLimit = atoi(argv[++i]);
        }
        else if (!strcmp(argv[i], "-f"))
        {
            force = true;
        }
        else if (!strcmp(argv[i], "--help"))
        {
            std::cerr << argv[0] << " [-t threads] [-f]" << std::endl <<
                "threads is the number of CPUs to use. Default=0. 0=All" << std::endl <<
                "Use -f to force it to use more than the number of CPUs" << std::endl;
            exit(1);
        }
        else
        {
            error((std::string("Invalid switch: ") + argv[i]).c_str());
        }
    }

    std::size_t cpuCount = std::thread::hardware_concurrency();

    if (threads <= 0)
    {
        std::cerr << "Using all " << cpuCount << " CPUs" << std::endl;
        threads = cpuCount;
    }

    if (threads > cpuCount)
    {
        if (force)
        {
            std::cerr << "Warning, forced to use more threads"
                         " than CPUs, " << threads << " threads,"
                         " but only " << cpuCount <<
                         " CPUs detected!" << std::endl;
        }
        else
        {
            std::cerr << "Capping to " << cpuCount <<
                    ". Use -f to override." << std::endl;
            threads = cpuCount;
        }
    }

    if (threads > max_threads)
    {
        std::cerr << "Capping to " << max_threads <<
                ". Recompile with higher max_threads." << std::endl;
        threads = max_threads;
    }

    return run_stress(threads, timeLimit);
}
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include <QtDeclarative>
#include <IrcSession>
#include <IrcCommand>
#include <IrcMessage>
#include <IrcPrefix>
#include <Irc>

class DeclarativeIrcPrefix : public QObject
{
    Q_OBJECT

public:
    explicit DeclarativeIrcPrefix(QObject* parent = 0) : QObject(parent) { }

    Q_INVOKABLE static QString name(const QString& prefix)
    {
        return IrcPrefix(prefix).name();
    }

    Q_INVOKABLE static QString user(const QString& prefix)
    {
        return IrcPrefix(prefix).user();
    }

    Q_INVOKABLE static QString host(const QString& prefix)
    {
        return IrcPrefix(prefix).host();
    }
};

QML_DECLARE_TYPE(Irc)
QML_DECLARE_TYPE(IrcCommand)
QML_DECLARE_TYPE(IrcMessage)
QML_DECLARE_TYPE(IrcSession)
QML_DECLARE_TYPE(DeclarativeIrcPrefix)

class CommuniPlugin : public QDeclarativeExtensionPlugin
{
    Q_OBJECT

public:
    virtual void initializeEngine(QDeclarativeEngine* engine, const char* uri)
    {
        QDeclarativeExtensionPlugin::initializeEngine(engine, uri);
    }

    void registerTypes(const char *uri)
    {
        qmlRegisterType<IrcSession>(uri, 1, 0, "IrcSession");
        qmlRegisterType<IrcCommand>(uri, 1, 0, "IrcCommand");

        qmlRegisterUncreatableType<Irc>(uri, 1, 0, "Irc", "");
        qmlRegisterUncreatableType<IrcMessage>(uri, 1, 0, "IrcMessage", "");
        qmlRegisterUncreatableType<DeclarativeIrcPrefix>(uri, 1, 0, "IrcPrefix", "");
    }
};

#include "plugin.moc"

Q_EXPORT_PLUGIN2(communiplugin, CommuniPlugin);
<commit_msg>Updated the declarative plugin<commit_after>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include <QtDeclarative>
#include <IrcSession>
#include <IrcCommand>
#include <IrcMessage>
#include <IrcPrefix>
#include <Irc>

class DeclarativeIrcPrefix : public QObject
{
    Q_OBJECT

public:
    explicit DeclarativeIrcPrefix(QObject* parent = 0) : QObject(parent) { }

    Q_INVOKABLE static QString name(const IrcPrefix& prefix)
    {
        return prefix.name();
    }

    Q_INVOKABLE static QString user(const IrcPrefix& prefix)
    {
        return prefix.user();
    }

    Q_INVOKABLE static QString host(const IrcPrefix& prefix)
    {
        return prefix.host();
    }
};

QML_DECLARE_TYPE(Irc)
QML_DECLARE_TYPE(IrcCommand)
QML_DECLARE_TYPE(IrcMessage)
QML_DECLARE_TYPE(IrcSession)
QML_DECLARE_TYPE(DeclarativeIrcPrefix)

class CommuniPlugin : public QDeclarativeExtensionPlugin
{
    Q_OBJECT

public:
    virtual void initializeEngine(QDeclarativeEngine* engine, const char* uri)
    {
        QDeclarativeExtensionPlugin::initializeEngine(engine, uri);

        QDeclarativeContext* context = engine->rootContext();
        context->setContextProperty("IrcPrefix", new DeclarativeIrcPrefix(context));
    }

    void registerTypes(const char *uri)
    {
        qmlRegisterType<IrcSession>(uri, 1, 0, "IrcSession");
        qmlRegisterType<IrcCommand>(uri, 1, 0, "IrcCommand");

        qmlRegisterUncreatableType<Irc>(uri, 1, 0, "Irc", "");
        qmlRegisterUncreatableType<IrcMessage>(uri, 1, 0, "IrcMessage", "");
    }
};

#include "plugin.moc"

Q_EXPORT_PLUGIN2(communiplugin, CommuniPlugin);
<|endoftext|>


<commit_before>fbef6fe8-ad5a-11e7-b436-ac87a332f658<commit_msg>oh my, I hate roselyn<commit_after>fc85b657-ad5a-11e7-9b50-ac87a332f658<|endoftext|>


<commit_before>#include <QWidget>
#include <QString>
#include <QFont>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QApplication>
#include <QEvent>
#include "textedit.h"

// ### TODO ###
// ### Should clear selection when something else selects something.
// ### Line break. Could vastly simplify textlayout if not breaking lines.
// ### saving to same file. Need something there.
// ### could refactor chunks so that I only have one and split when I need. Not sure if it's worth it. Would
// ### need chunkData(int from = 0, int size = -1). The current approach makes caching easier since I can now cache an entire chunk.
// ### ensureCursorVisible(TextCursor) is not implemented
// ### add a command line switch to randomly do stuff. Insert, move around etc n times to see if I can reproduce a crash. Allow input of seed
// ### I still have some weird debris when scrolling in large documents
// ### use tests from Qt. E.g. for QTextCursor
// ### need to protect against undo with huge amounts of text in it. E.g. select all and delete. MsgBox asking?
// ### maybe refactor updatePosition so it can handle the margins usecase that textcursor needs.
// ### could keep undo/redo history in the textcursor and have an api on the cursor.
// ### block state in SyntaxHighlighter doesn't remember between
// ### highlights. In regular QText.* it does. Could store this info.
// ### Shouldn't be that much. Maybe base it off of position in
// ### document rather than line number since I don't know which line
// ### we're on
// ### TextCursor(const TextEdit *). Is this a good idea?
// ### caching stuff is getting a little convoluted
// ### what should TextDocument::read(documentSize + 1, 10) do? ASSERT?
// ### should I allow to write in a formatted manner? currentTextFormat and all? Why not really.
// ### consider using QTextBoundaryFinder for something
// ### drag and drop
// ### documentation
// ### consider having extra textLayouts on each side of viewport for optimized scrolling. Could detect that condition
// ### Undo section removal/adding. This is a mess

class Highlighter : public SyntaxHighlighter
{
public:
    Highlighter(QWidget *parent)
        : SyntaxHighlighter(parent)
    {

    }

    void helper(int from, int size, bool blackForeground)
    {
        QTextCharFormat format;
        format.setBackground(blackForeground ? Qt::yellow : Qt::black);
        format.setForeground(blackForeground ? Qt::black : Qt::yellow);
        setFormat(from, size, format);
//        qDebug() << "setting format" << from << size << currentBlock().mid(from, size) << blackForeground;
    }

    virtual void highlightBlock(const QString &text)
    {
        enum { Space, LetterOrNumber, Other } state = Space;
        int last = 0;
        for (int i=0; i<text.size(); ++i) {
            if (text.at(i).isSpace()) {
                if (state != Space)
                    helper(last, i - last, state == LetterOrNumber);
                state = Space;
                last = i;
            } else if (text.at(i).isLetterOrNumber()
                       != (state == LetterOrNumber) || state == Space) {
                if (state != Space) {
                    helper(last, i - last, state == LetterOrNumber);
                }
                state = (text.at(i).isLetterOrNumber()
                         ? LetterOrNumber
                         : Other);
                last = i;
            }
        }
        if (state != Space) {
            helper(last, text.size() - last, state == LetterOrNumber);
        }
    }
};

bool add = false;
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0)
        : QMainWindow(parent), doLineNumbers(false)
    {
        QString fileName = "main.cpp";
        bool replay = false;
        bool readOnly = false;
        int chunkSize = -1;
        QTextCodec *codec = 0;
        const QStringList list = QApplication::arguments().mid(1);
        for (int i=0; i<list.size(); ++i) {
            const QString &arg = list.at(i);
            if (arg == "--replay") {
                replay = true;
                fileName.clear();
            } else if (arg == "--add") {
                replay = true;
                add = true;
                fileName.clear();
            } else if (arg == "--log") {
                fileName.clear();
                appendTimer.start(10, this);
                readOnly = true;
                chunkSize = 1000;
            } else if (arg == "--readonly") {
                readOnly = true;
            } else if (arg == "--linenumbers") {
                doLineNumbers = true;
            } else if (arg.startsWith("--codec=")) {
                codec = QTextCodec::codecForName(arg.mid(8).toLatin1());
                if (!codec) {
                    qWarning("Can't load codec called '%s'", qPrintable(arg.mid(8)));
                    exit(1);
                }
            } else if (arg.startsWith("--chunksize=")) {
                bool ok;
                chunkSize = arg.mid(12).toInt(&ok);
                if (!ok) {
                    qWarning("Can't parse %s", qPrintable(arg));
                    exit(1);
                }
            } else {
                fileName = arg;
            }
        }
        if (fileName.isEmpty() && !appendTimer.isActive()) {
            Q_ASSERT(replay);
            QStringList list = QDir(".").entryList(QStringList() << "*.log");
            if (list.isEmpty()) {
                qWarning("Nothing to replay");
                exit(1);
            }
            qSort(list);
            fileName = list.last();
        }
        if (replay) {
            QFile file(fileName);
            if (!file.open(QIODevice::ReadOnly)) {
                qWarning("Can't open '%s' for reading", qPrintable(fileName));
                exit(1);
            }
#ifndef QT_NO_DEBUG_STREAM
            qDebug() << "replaying" << fileName;
#endif
#ifndef QT_NO_DEBUG
            if (add) {
                extern QString logFileName;
                logFileName = fileName;
            }
            extern bool doLog; // from textedit.cpp
            doLog = false;
#endif
            QDataStream ds(&file);
            ds >> fileName; // reuse this variable for loading document. First QString in log file
            while (!file.atEnd()) {
                int type;
                ds >> type;
                if (type == QEvent::KeyPress || type == QEvent::KeyRelease) {
                    int key;
                    int modifiers;
                    QString text;
                    bool isAutoRepeat;
                    int count;
                    ds >> key;
                    ds >> modifiers;
                    ds >> text;
                    ds >> isAutoRepeat;
                    ds >> count;
                    events.append(new QKeyEvent(static_cast<QEvent::Type>(type), key,
                                                static_cast<Qt::KeyboardModifiers>(modifiers),
                                                text, isAutoRepeat, count));
                } else if (type == QEvent::Resize) {
                    QSize size;
                    ds >> size;
                    events.append(new QResizeEvent(size, QSize()));
                } else {
                    Q_ASSERT(type == QEvent::MouseMove || type == QEvent::MouseButtonPress || type == QEvent::MouseButtonRelease);
                    QPoint pos;
                    int button;
                    int buttons;
                    int modifiers;
                    ds >> pos;
                    ds >> button;
                    ds >> buttons;
                    ds >> modifiers;
                    events.append(new QMouseEvent(static_cast<QEvent::Type>(type), pos,
                                                  static_cast<Qt::MouseButton>(button),
                                                  static_cast<Qt::MouseButtons>(buttons),
                                                  static_cast<Qt::KeyboardModifiers>(modifiers)));
                }
            }
            if (!events.isEmpty())
                QTimer::singleShot(0, this, SLOT(sendNextEvent()));
        }

        QWidget *w = new QWidget(this);
        QVBoxLayout *l = new QVBoxLayout(w);
        setCentralWidget(w);
        l->addWidget(textEdit = new TextEdit(w));
        textEdit->setObjectName("primary");
        if (chunkSize != -1) {
            textEdit->document()->setChunkSize(chunkSize);
        }
        l->addWidget(otherEdit = new TextEdit);
        otherEdit->setReadOnly(true);
        otherEdit->setObjectName("otherEdit");
        textEdit->setReadOnly(readOnly);
        QFontDatabase fdb;
        foreach(QString family, fdb.families()) {
            if (fdb.isFixedPitch(family)) {
                QFont f(family);
                textEdit->setFont(f);
                break;
            }
        }

//        textEdit->setSyntaxHighlighter(new Highlighter(textEdit));
#ifndef QT_NO_DEBUG_STREAM
        if (codec) {
            qDebug() << "using codec" << codec->name();
        }
#endif
        if (appendTimer.isActive())
            textEdit->document()->setOption(TextDocument::SwapChunks, true);
        if (!fileName.isEmpty() && !textEdit->load(fileName, TextDocument::Sparse, codec)) {
#ifndef QT_NO_DEBUG_STREAM
            qDebug() << "Can't load" << fileName;
#endif
        }
        otherEdit->setDocument(textEdit->document());

        lbl = new QLabel(w);
        connect(textEdit, SIGNAL(cursorPositionChanged(int)),
                this, SLOT(onCursorPositionChanged(int)));
        l->addWidget(lbl);

        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_E), textEdit, SLOT(ensureCursorVisible()));
        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_L), this, SLOT(createTextSection()));
        new QShortcut(QKeySequence(QKeySequence::Close), this, SLOT(close()));
        new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(changeTextSectionFormat()));

        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_S), this, SLOT(save()));
        new QShortcut(QKeySequence(Qt::ALT + Qt::Key_S), this, SLOT(saveAs()));

        QMenu *menu = menuBar()->addMenu("&File");
        menu->addAction("About textedit", this, SLOT(about()));

        QHBoxLayout *h = new QHBoxLayout;
        h->addWidget(box = new QSpinBox(centralWidget()));
        connect(textEdit->verticalScrollBar(), SIGNAL(valueChanged(int)),
                this, SLOT(onScrollBarValueChanged()));

        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G), this, SLOT(gotoPos()));

        box->setReadOnly(true);
        box->setRange(0, INT_MAX);
        l->addLayout(h);

        connect(textEdit, SIGNAL(sectionClicked(TextSection *, QPoint)), this, SLOT(onTextSectionClicked(TextSection *, QPoint)));

        textEdit->viewport()->setAutoFillBackground(true);
        connect(textEdit->document(), SIGNAL(modificationChanged(bool)), this, SLOT(onModificationChanged(bool)));
    }

    void timerEvent(QTimerEvent *e)
    {
        if (e->timerId() == appendTimer.timerId()) {
            static int line = 0;
            textEdit->append(QString("This is line number %1\n").arg(++line));
            TextCursor curs = textEdit->textCursor();
            curs.movePosition(TextCursor::End);
            curs.movePosition(TextCursor::PreviousBlock);
            textEdit->setTextCursor(curs);
            textEdit->ensureCursorVisible();
            if (line % 100 == 0) {
                qDebug() << "memory used" << textEdit->document()->currentMemoryUsage()
                         << "documentSize" << textEdit->document()->documentSize();
            }
        } else {
            QMainWindow::timerEvent(e);
        }
    }

    void closeEvent(QCloseEvent *e)
    {
        QSettings("LazyTextEditor", "LazyTextEditor").setValue("geometry", saveGeometry());
        QMainWindow::closeEvent(e);
    }
    void showEvent(QShowEvent *e)
    {
        activateWindow();
        raise();
#ifndef QT_NO_DEBUG
        extern bool doLog; // from textedit.cpp
        if (doLog)
#endif
            restoreGeometry(QSettings("LazyTextEditor", "LazyTextEditor").value("geometry").toByteArray());
        QMainWindow::showEvent(e);
    }
public slots:
    void onModificationChanged(bool on)
    {
        QPalette pal = textEdit->viewport()->palette();
        pal.setColor(textEdit->viewport()->backgroundRole(), on ? Qt::yellow : Qt::white);
        textEdit->viewport()->setPalette(pal);
    }

    void save()
    {
        textEdit->document()->save();
    }

    void saveAs()
    {
        const QString str = QFileDialog::getSaveFileName(this, "Save as", ".");
        if (!str.isEmpty())
            textEdit->document()->save(str);
    }
    void about()
    {
        textEdit->textCursor().setPosition(0);
        QMessageBox::information(this, "Textedit", QString("Current memory usage %1KB").
                                 arg(textEdit->document()->currentMemoryUsage() / 1024),
                                 QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
    }

    void onCursorPositionChanged(int pos)
    {
        QString text = QString("Position: %1\n"
                               "Word: %2\n").
                       arg(pos).
                       arg(textEdit->textCursor().wordUnderCursor());
        if (doLineNumbers)
            text += QString("Line number: %0").arg(textEdit->document()->lineNumber(pos));
        lbl->setText(text);
    }

    void sendNextEvent()
    {
        QEvent *ev = events.takeFirst();
        QWidget *target = textEdit->viewport();
        switch (ev->type()) {
        case QEvent::Resize:
            resize(static_cast<QResizeEvent*>(ev)->size());
            break;
        case QEvent::KeyPress:
            if (static_cast<QKeyEvent*>(ev)->matches(QKeySequence::Close))
                break;
            // fall through
            target = textEdit;
        default:
            QApplication::postEvent(target, ev);
            break;
        }

        if (!events.isEmpty()) {
            QTimer::singleShot(0, this, SLOT(sendNextEvent()));
#ifndef QT_NO_DEBUG
        } else if (add) {
            extern bool doLog; // from textedit.cpp
            doLog = true;
#endif
        }
    }

    void createTextSection()
    {
        TextCursor cursor = textEdit->textCursor();
        if (cursor.hasSelection()) {
            static bool first = true;
            QTextCharFormat format;
            if (first) {
                format.setForeground(Qt::blue);
                format.setFontUnderline(true);
            } else {
                format.setBackground(Qt::black);
                format.setForeground(Qt::white);
            }
            const int pos = cursor.selectionStart();
            const int size = cursor.selectionEnd() - pos;
            TextSection *s = 0;
            if (first) {
                s = textEdit->insertTextSection(pos, size, format, cursor.selectedText());
                Q_ASSERT(!otherEdit->sections().contains(s));
                Q_ASSERT(!s || textEdit->sections().contains(s));
            } else {
                s = textEdit->document()->insertTextSection(pos, size, format, cursor.selectedText());
            }
            first = !first;
            if (s) {
                s->setCursor(Qt::PointingHandCursor);
                Q_UNUSED(s);
                Q_ASSERT(s);
            }
        }
    }

    void changeTextSectionFormat()
    {
        static bool first = true;
        QTextCharFormat format;
        format.setFontUnderline(true);
        if (first) {
            format.setForeground(Qt::white);
            format.setBackground(Qt::blue);
        } else {
            format.setForeground(Qt::blue);
        }
        first = !first;
        foreach(TextSection *s, textEdit->sections()) {
            s->setFormat(format);
        }

    }
    void onTextSectionClicked(TextSection *section, const QPoint &pos)
    {
#ifndef QT_NO_DEBUG_STREAM
        qDebug() << section->text() << section->data() << pos;
#endif
    }
    void onScrollBarValueChanged()
    {
        box->setValue(textEdit->viewportPosition());
    }
    void gotoPos()
    {
        TextCursor &cursor = textEdit->textCursor();
        bool ok;
        int pos = QInputDialog::getInteger(this, "Goto pos", "Pos", cursor.position(), 0,
                                           textEdit->document()->documentSize(), 1, &ok);
        if (!ok)
            return;
        cursor.setPosition(pos);
    }

private:
    QSpinBox *box;
    TextEdit *textEdit, *otherEdit;
    QLabel *lbl;
    QLinkedList<QEvent*> events;
    bool doLineNumbers;
    QBasicTimer appendTimer;
};

#include "main.moc"

int main(int argc, char **argv)
{
    QApplication a(argc, argv);
    MainWindow w;
    w.resize(500, 100);
    w.show();
    return a.exec();
}
<commit_msg>extra debugging things<commit_after>#include <QWidget>
#include <QString>
#include <QFont>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QApplication>
#include <QEvent>
#include "textedit.h"

// ### TODO ###
// ### Should clear selection when something else selects something.
// ### Line break. Could vastly simplify textlayout if not breaking lines.
// ### saving to same file. Need something there.
// ### could refactor chunks so that I only have one and split when I need. Not sure if it's worth it. Would
// ### need chunkData(int from = 0, int size = -1). The current approach makes caching easier since I can now cache an entire chunk.
// ### ensureCursorVisible(TextCursor) is not implemented
// ### add a command line switch to randomly do stuff. Insert, move around etc n times to see if I can reproduce a crash. Allow input of seed
// ### I still have some weird debris when scrolling in large documents
// ### use tests from Qt. E.g. for QTextCursor
// ### need to protect against undo with huge amounts of text in it. E.g. select all and delete. MsgBox asking?
// ### maybe refactor updatePosition so it can handle the margins usecase that textcursor needs.
// ### could keep undo/redo history in the textcursor and have an api on the cursor.
// ### block state in SyntaxHighlighter doesn't remember between
// ### highlights. In regular QText.* it does. Could store this info.
// ### Shouldn't be that much. Maybe base it off of position in
// ### document rather than line number since I don't know which line
// ### we're on
// ### TextCursor(const TextEdit *). Is this a good idea?
// ### caching stuff is getting a little convoluted
// ### what should TextDocument::read(documentSize + 1, 10) do? ASSERT?
// ### should I allow to write in a formatted manner? currentTextFormat and all? Why not really.
// ### consider using QTextBoundaryFinder for something
// ### drag and drop
// ### documentation
// ### consider having extra textLayouts on each side of viewport for optimized scrolling. Could detect that condition
// ### Undo section removal/adding. This is a mess

class Highlighter : public SyntaxHighlighter
{
public:
    Highlighter(QWidget *parent)
        : SyntaxHighlighter(parent)
    {

    }

    void helper(int from, int size, bool blackForeground)
    {
        QTextCharFormat format;
        format.setBackground(blackForeground ? Qt::yellow : Qt::black);
        format.setForeground(blackForeground ? Qt::black : Qt::yellow);
        setFormat(from, size, format);
//        qDebug() << "setting format" << from << size << currentBlock().mid(from, size) << blackForeground;
    }

    virtual void highlightBlock(const QString &text)
    {
        enum { Space, LetterOrNumber, Other } state = Space;
        int last = 0;
        for (int i=0; i<text.size(); ++i) {
            if (text.at(i).isSpace()) {
                if (state != Space)
                    helper(last, i - last, state == LetterOrNumber);
                state = Space;
                last = i;
            } else if (text.at(i).isLetterOrNumber()
                       != (state == LetterOrNumber) || state == Space) {
                if (state != Space) {
                    helper(last, i - last, state == LetterOrNumber);
                }
                state = (text.at(i).isLetterOrNumber()
                         ? LetterOrNumber
                         : Other);
                last = i;
            }
        }
        if (state != Space) {
            helper(last, text.size() - last, state == LetterOrNumber);
        }
    }
};

bool add = false;
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0)
        : QMainWindow(parent), doLineNumbers(false)
    {
        QString fileName = "main.cpp";
        bool replay = false;
        bool readOnly = false;
        int chunkSize = -1;
        QTextCodec *codec = 0;
        const QStringList list = QApplication::arguments().mid(1);
        for (int i=0; i<list.size(); ++i) {
            const QString &arg = list.at(i);
            if (arg == "--replay") {
                replay = true;
                fileName.clear();
            } else if (arg == "--add") {
                replay = true;
                add = true;
                fileName.clear();
            } else if (arg == "--log") {
                fileName.clear();
                appendTimer.start(10, this);
                readOnly = true;
                chunkSize = 1000;
            } else if (arg == "--readonly") {
                readOnly = true;
            } else if (arg == "--linenumbers") {
                doLineNumbers = true;
            } else if (arg.startsWith("--codec=")) {
                codec = QTextCodec::codecForName(arg.mid(8).toLatin1());
                if (!codec) {
                    qWarning("Can't load codec called '%s'", qPrintable(arg.mid(8)));
                    exit(1);
                }
            } else if (arg.startsWith("--chunksize=")) {
                bool ok;
                chunkSize = arg.mid(12).toInt(&ok);
                if (!ok) {
                    qWarning("Can't parse %s", qPrintable(arg));
                    exit(1);
                }
            } else {
                fileName = arg;
            }
        }
        if (fileName.isEmpty() && !appendTimer.isActive()) {
            Q_ASSERT(replay);
            QStringList list = QDir(".").entryList(QStringList() << "*.log");
            if (list.isEmpty()) {
                qWarning("Nothing to replay");
                exit(1);
            }
            qSort(list);
            fileName = list.last();
        }
        if (replay) {
            QFile file(fileName);
            if (!file.open(QIODevice::ReadOnly)) {
                qWarning("Can't open '%s' for reading", qPrintable(fileName));
                exit(1);
            }
#ifndef QT_NO_DEBUG_STREAM
            qDebug() << "replaying" << fileName;
#endif
#ifndef QT_NO_DEBUG
            if (add) {
                extern QString logFileName;
                logFileName = fileName;
            }
            extern bool doLog; // from textedit.cpp
            doLog = false;
#endif
            QDataStream ds(&file);
            ds >> fileName; // reuse this variable for loading document. First QString in log file
            while (!file.atEnd()) {
                int type;
                ds >> type;
                if (type == QEvent::KeyPress || type == QEvent::KeyRelease) {
                    int key;
                    int modifiers;
                    QString text;
                    bool isAutoRepeat;
                    int count;
                    ds >> key;
                    ds >> modifiers;
                    ds >> text;
                    ds >> isAutoRepeat;
                    ds >> count;
                    events.append(new QKeyEvent(static_cast<QEvent::Type>(type), key,
                                                static_cast<Qt::KeyboardModifiers>(modifiers),
                                                text, isAutoRepeat, count));
                } else if (type == QEvent::Resize) {
                    QSize size;
                    ds >> size;
                    events.append(new QResizeEvent(size, QSize()));
                } else {
                    Q_ASSERT(type == QEvent::MouseMove || type == QEvent::MouseButtonPress || type == QEvent::MouseButtonRelease);
                    QPoint pos;
                    int button;
                    int buttons;
                    int modifiers;
                    ds >> pos;
                    ds >> button;
                    ds >> buttons;
                    ds >> modifiers;
                    events.append(new QMouseEvent(static_cast<QEvent::Type>(type), pos,
                                                  static_cast<Qt::MouseButton>(button),
                                                  static_cast<Qt::MouseButtons>(buttons),
                                                  static_cast<Qt::KeyboardModifiers>(modifiers)));
                }
            }
            if (!events.isEmpty())
                QTimer::singleShot(0, this, SLOT(sendNextEvent()));
        }

        QWidget *w = new QWidget(this);
        QVBoxLayout *l = new QVBoxLayout(w);
        setCentralWidget(w);
        l->addWidget(textEdit = new TextEdit(w));
        textEdit->setObjectName("primary");
        if (chunkSize != -1) {
            textEdit->document()->setChunkSize(chunkSize);
        }
        l->addWidget(otherEdit = new TextEdit);
        otherEdit->setReadOnly(true);
        otherEdit->hide();
        otherEdit->setObjectName("otherEdit");
        textEdit->setReadOnly(readOnly);
        QFontDatabase fdb;
        foreach(QString family, fdb.families()) {
            if (fdb.isFixedPitch(family)) {
                QFont f(family);
                textEdit->setFont(f);
                break;
            }
        }

//        textEdit->setSyntaxHighlighter(new Highlighter(textEdit));
#ifndef QT_NO_DEBUG_STREAM
        if (codec) {
            qDebug() << "using codec" << codec->name();
        }
#endif
        if (appendTimer.isActive())
            textEdit->document()->setOption(TextDocument::SwapChunks, true);
        if (!fileName.isEmpty() && !textEdit->load(fileName, TextDocument::Sparse, codec)) {
#ifndef QT_NO_DEBUG_STREAM
            qDebug() << "Can't load" << fileName;
#endif
        }
#if 0
        textEdit->document()->setText("This is a test");
        QTextCharFormat format;
        format.setBackground(Qt::yellow);
        textEdit->insertTextSection(0, 4, format);
        format = QTextCharFormat();
        format.setFontItalic(true);
        textEdit->insertTextSection(0, 4, format);
#endif
        otherEdit->setDocument(textEdit->document());

        lbl = new QLabel(w);
        connect(textEdit, SIGNAL(cursorPositionChanged(int)),
                this, SLOT(onCursorPositionChanged(int)));
        l->addWidget(lbl);

        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_E), textEdit, SLOT(ensureCursorVisible()));
        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_L), this, SLOT(createTextSection()));
        new QShortcut(QKeySequence(QKeySequence::Close), this, SLOT(close()));
        new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(changeTextSectionFormat()));

        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_S), this, SLOT(save()));
        new QShortcut(QKeySequence(Qt::ALT + Qt::Key_S), this, SLOT(saveAs()));

        QMenu *menu = menuBar()->addMenu("&File");
        menu->addAction("About textedit", this, SLOT(about()));

        QHBoxLayout *h = new QHBoxLayout;
        h->addWidget(box = new QSpinBox(centralWidget()));
        connect(textEdit->verticalScrollBar(), SIGNAL(valueChanged(int)),
                this, SLOT(onScrollBarValueChanged()));

        new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G), this, SLOT(gotoPos()));

        box->setReadOnly(true);
        box->setRange(0, INT_MAX);
        l->addLayout(h);

        connect(textEdit, SIGNAL(sectionClicked(TextSection *, QPoint)), this, SLOT(onTextSectionClicked(TextSection *, QPoint)));

        textEdit->viewport()->setAutoFillBackground(true);
        connect(textEdit->document(), SIGNAL(modificationChanged(bool)), this, SLOT(onModificationChanged(bool)));
    }

    void timerEvent(QTimerEvent *e)
    {
        if (e->timerId() == appendTimer.timerId()) {
            static int line = 0;
            textEdit->append(QString("This is line number %1\n").arg(++line));
            TextCursor curs = textEdit->textCursor();
            curs.movePosition(TextCursor::End);
            curs.movePosition(TextCursor::PreviousBlock);
            textEdit->setTextCursor(curs);
            textEdit->ensureCursorVisible();
            if (line % 100 == 0) {
                qDebug() << "memory used" << textEdit->document()->currentMemoryUsage()
                         << "documentSize" << textEdit->document()->documentSize();
            }
        } else {
            QMainWindow::timerEvent(e);
        }
    }

    void closeEvent(QCloseEvent *e)
    {
        QSettings("LazyTextEditor", "LazyTextEditor").setValue("geometry", saveGeometry());
        QMainWindow::closeEvent(e);
    }
    void showEvent(QShowEvent *e)
    {
        activateWindow();
        raise();
#ifndef QT_NO_DEBUG
        extern bool doLog; // from textedit.cpp
        if (doLog)
#endif
            restoreGeometry(QSettings("LazyTextEditor", "LazyTextEditor").value("geometry").toByteArray());
        QMainWindow::showEvent(e);
    }
public slots:
    void onModificationChanged(bool on)
    {
        QPalette pal = textEdit->viewport()->palette();
        pal.setColor(textEdit->viewport()->backgroundRole(), on ? Qt::yellow : Qt::white);
        textEdit->viewport()->setPalette(pal);
    }

    void save()
    {
        textEdit->document()->save();
    }

    void saveAs()
    {
        const QString str = QFileDialog::getSaveFileName(this, "Save as", ".");
        if (!str.isEmpty())
            textEdit->document()->save(str);
    }
    void about()
    {
        textEdit->textCursor().setPosition(0);
        QMessageBox::information(this, "Textedit", QString("Current memory usage %1KB").
                                 arg(textEdit->document()->currentMemoryUsage() / 1024),
                                 QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
    }

    void onCursorPositionChanged(int pos)
    {
        QString text = QString("Position: %1\n"
                               "Word: %2\n").
                       arg(pos).
                       arg(textEdit->textCursor().wordUnderCursor());
        if (doLineNumbers)
            text += QString("Line number: %0").arg(textEdit->document()->lineNumber(pos));
        lbl->setText(text);
    }

    void sendNextEvent()
    {
        QEvent *ev = events.takeFirst();
        QWidget *target = textEdit->viewport();
        switch (ev->type()) {
        case QEvent::Resize:
            resize(static_cast<QResizeEvent*>(ev)->size());
            break;
        case QEvent::KeyPress:
            if (static_cast<QKeyEvent*>(ev)->matches(QKeySequence::Close))
                break;
            // fall through
            target = textEdit;
        default:
            QApplication::postEvent(target, ev);
            break;
        }

        if (!events.isEmpty()) {
            QTimer::singleShot(0, this, SLOT(sendNextEvent()));
#ifndef QT_NO_DEBUG
        } else if (add) {
            extern bool doLog; // from textedit.cpp
            doLog = true;
#endif
        }
    }

    void createTextSection()
    {
        TextCursor cursor = textEdit->textCursor();
        if (cursor.hasSelection()) {
            static bool first = true;
            QTextCharFormat format;
            if (first) {
                format.setForeground(Qt::blue);
                format.setFontUnderline(true);
            } else {
                format.setBackground(Qt::black);
                format.setForeground(Qt::white);
            }
            const int pos = cursor.selectionStart();
            const int size = cursor.selectionEnd() - pos;
            TextSection *s = 0;
            if (first) {
                s = textEdit->insertTextSection(pos, size, format, cursor.selectedText());
                if (s) {
                    s->setCursor(Qt::PointingHandCursor);
                    Q_UNUSED(s);
                    Q_ASSERT(s);
                }

                Q_ASSERT(!otherEdit->sections().contains(s));
                Q_ASSERT(!s || textEdit->sections().contains(s));
            } else {
                s = textEdit->document()->insertTextSection(pos, size, format, cursor.selectedText());
            }
            first = !first;
        }
    }

    void changeTextSectionFormat()
    {
        static bool first = true;
        QTextCharFormat format;
        format.setFontUnderline(true);
        if (first) {
            format.setForeground(Qt::white);
            format.setBackground(Qt::blue);
        } else {
            format.setForeground(Qt::blue);
        }
        first = !first;
        foreach(TextSection *s, textEdit->sections()) {
            s->setFormat(format);
        }

    }
    void onTextSectionClicked(TextSection *section, const QPoint &pos)
    {
#ifndef QT_NO_DEBUG_STREAM
        qDebug() << section->text() << section->data() << pos;
#endif
    }
    void onScrollBarValueChanged()
    {
        box->setValue(textEdit->viewportPosition());
    }
    void gotoPos()
    {
        TextCursor &cursor = textEdit->textCursor();
        bool ok;
        int pos = QInputDialog::getInteger(this, "Goto pos", "Pos", cursor.position(), 0,
                                           textEdit->document()->documentSize(), 1, &ok);
        if (!ok)
            return;
        cursor.setPosition(pos);
    }

private:
    QSpinBox *box;
    TextEdit *textEdit, *otherEdit;
    QLabel *lbl;
    QLinkedList<QEvent*> events;
    bool doLineNumbers;
    QBasicTimer appendTimer;
};

#include "main.moc"

int main(int argc, char **argv)
{
    QApplication a(argc, argv);
    MainWindow w;
    w.resize(500, 100);
    w.show();
    return a.exec();
}
<|endoftext|>


<commit_before>// rump_equation.cpp: example program to show Rump computation requiring high-precision floats to work
//
// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <ostream>
#include <limits>
#include <numeric>   // nextafter

// select the number systems we would like to compare
#include <universal/number/integer/integer>
#include <universal/number/fixpnt/fixpnt>
#include <universal/number/areal/areal>
//#include <universal/number/bfloat/bfloat>
#include <universal/number/posit/posit>
#include <universal/number/lns/lns>

#include <universal/number/posit/numeric_limits.hpp>

template<typename Scalar>
Scalar Rump(const Scalar& a, const Scalar& b) {
	Scalar b2 = b * b;
	Scalar b3 = b * b * b;
	Scalar b4 = b2 * b2;
	Scalar b6 = b3 * b3;
	Scalar b8 = b4 * b4;
	Scalar a2 = a * a;
	// 333.75 * b ^ 6 + a ^ 2 * (11 * a ^ 2 * b ^ 2 - b ^ 6 - 121 * b ^ 4 - 2) + 5.5 * b ^ 8 + a / (2 * b);
	return 333.75 * b6 + a2 * (11 * a2 * b2 - b6 - 121 * b4 - 2) + 5.5 * b8 + a / (2 * b);
}

int main(int argc, char** argv)
try {
	using namespace std;
	using namespace sw::universal;

	cout << "Rump's equation\n";

/*
 * one off constant computation from "Handbook of Floating-Point Arithmetic":
     f(a,b) = 333.75*b^6 + a^2*(11*a^2*b^2 - b^6 - 121*b^4 - 2) + 5.5*b^8 + a/(2*b)
     for a=77617.0, b=33096.0
The exact result is -54767 / 66192 which starts with −0.8273960599...
Using pow vs repeated mutiplication for the power expressions usually yields the same answer, but not always
Running on x86 fp types we get (picking some interesting MPFR results)
Type        | Rep Mult    | Pow Func
------------+-------------+-------------
float       | -6.3383E+29 | -6.3383E+29
double      | -1.1806E+21 | -1.1806E+21
long double |  1.1726     |  5.7646E+17
quad        |  1.1726     |  1.1726
mpfr(26)    |  1.5846E+29 | -1.5846E+29
mpfr(37)    |  1.1726     | -1.5474E+26
mpfr(54)    |  1.1806e+21 |  1.1726
mpfr(76)    |  1.1726     |  1.1726
mpfr(98)    |  3.3554E+07 |  3.3554E+07
mpfr(121)   |  1.1726     |  1.1726
mpfr(122)   | -0.8274     | -0.8274
It requires 122 bits of mantissa under MPFR to get the correct answer, and is really erratic at "lower" precision.
There are a couple fpbench versions of it:  https://fpbench.org/benchmarks.html#Rump's%20example
*/

	streamsize precision = cout.precision();
	double da = 77617.0;
	double db = 33096.0;

	{
		posit<128, 2> a{ da }, b{ db };
		cout << double(Rump(a, b)) << endl;
	}
	{
		posit<156, 2> a{ da }, b{ db };
		cout << double(Rump(a, b)) << endl;
	}
	{
		posit<256, 2> a{ da }, b{ db };
		cout << double(Rump(a, b)) << endl;
	}

	cout << setprecision(precision);
	cout << endl;

	return EXIT_SUCCESS;
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const std::runtime_error& err) {
	std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}
<commit_msg>experiments with the Rump equation and different number systems<commit_after>// rump_equation.cpp: example program to show Rump computation requiring high-precision floats to work
//
// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <ostream>
#include <limits>
#include <numeric>   // nextafter

// select the number systems we would like to compare
#include <universal/number/decimal/decimal>
#include <universal/number/integer/integer>
#include <universal/number/fixpnt/fixpnt>
#include <universal/number/areal/areal>
#include <universal/number/bfloat/bfloat>
#include <universal/number/posit/posit>
#include <universal/number/lns/lns>

#include <universal/number/posit/numeric_limits.hpp>

#include <universal/blas/blas.hpp>

/*
In 1988, Siegfried Rump published an example in which numerical evaluation of an expression
gave a misleading result, even though use of increasing arithmetic precision suggested reliable
computation.

Rump's example is to compute the expression:
    f(a,b) =  333.75 * b ^ 6 + a ^ 2 * (11 * a ^ 2 * b ^ 2 - b ^ 6 - 121 * b ^ 4 - 2) + 5.5 * b ^ 8 + a / (2 * b)
with a = 77617 and b 33096.

On an IBM S/370 main frame the function evaluates to the following values given the labeled
precision:
   single precision           f = 1.172603...
   double precision           f = 1.1726039400531...
   extended precision         f = 1.172603949953178...
This creates the illusion of a reliable result of approximately 1.172603. But in fact, the
correct  result is:
   correct result             f = -0.827396059946821368141165095479816...

   Using IEEE-754, we get the following results:
			    type  |                 Rump1  |                 Rump2  |                 Rump3  |
			   float  |           2.80149e+29  |                1.1726  |             -0.827396  |
			  double  |          -1.18059e+21  |                1.1726  |             -0.827396  |
		 long double  |          -1.18059e+21  |                1.1726  |             -0.827396  |

The root cause of this behavior is catastrophic cancellation due to the large scale of the exponentiation terms.
The value of a and b satisfy the equation:
           a ^ 2 = 5.5 * b ^ 2  +  1
Simple algebraic manipulation yields the more transparent form of the computation

    f(a,b) = 5.5 * b ^ 8 - 1 - 5.5 * b ^ 8 + a / ( 2 * b)

In this form it is easy to see where the cancellation occurs. The large term 5.5b^8 cancels out leaving
the quation:

    f(a,b) = -2 + a / (2 * b), which yields the correct value of -0.827396... for most formats.

For any arithmetic to evaluate this function in its raw form requires enough precision bits to
represent the value 1.0 in the ULP. 5.5*b^8 at b = 33096 is of the order of 8e+36, which requires
122 bits of precision to capture the -2.0 while still representing 8e+36 and thus avoiding
catastrophic cancellation of this -2.0 during the computation.

*/
// original f(a,b) =  333.75 * b ^ 6 + a ^ 2 * (11 * a ^ 2 * b ^ 2 - b ^ 6 - 121 * b ^ 4 - 2) + 5.5 * b ^ 8 + a / (2 * b);
template<typename Scalar>
Scalar Rump1(double _a, double _b) {
	Scalar a, b;
	a = Scalar(_a);
	b = Scalar(_b);
	Scalar b2 = b * b;
	Scalar b3 = b * b * b;
	Scalar b4 = b2 * b2;
	Scalar b6 = b3 * b3;
	Scalar b8 = b4 * b4;
	Scalar a2 = a * a;
	// 333.75 * b ^ 6 + a ^ 2 * (11 * a ^ 2 * b ^ 2 - b ^ 6 - 121 * b ^ 4 - 2) + 5.5 * b ^ 8 + a / (2 * b);
	return 333.75 * b6 + a2 * (11 * a2 * b2 - b6 - 121 * b4 - 2) + 5.5 * b8 + a / (2 * b);
}

template<typename Scalar>
Scalar TraceRump1(double _a, double _b) {
	std::cout << "+-----------------------------------------------------------------------------\n" << typeid(Scalar).name() << '\n';
	Scalar a, b;
	a = Scalar(_a);
	b = Scalar(_b);
	Scalar b2 = b * b;
	std::cout << "b * b                              : " << b2 << '\n';
	Scalar b3 = b * b * b;
	std::cout << "b * b * b                          : " << b3 << '\n';
	Scalar b4 = b2 * b2;
	std::cout << "b * b * b * b                      : " << b4 << '\n';
	Scalar b6 = b3 * b3;
	std::cout << "b3 * b3                            : " << b6 << '\n';
	Scalar b8 = b4 * b4;
	std::cout << "b4 * b4                            : " << b8 << '\n';
	Scalar a2 = a * a;
	std::cout << "a * a                              : " << a2 << '\n';

	Scalar term1 = 333.75 * b6;
	std::cout << "333.75 * b6                  term1 : " << term1 << '\n';
	Scalar term2 = 11 * a2 * b2;
	std::cout << "11 * a2 * b2                       : " << term2 << '\n';
	Scalar term3 = (11 * a2 * b2 - b6 - 121 * b4 - 2);
	std::cout << "(11 * a2 * b2 - b6 - 121 * b4 - 2) : " << term3 << '\n';

	Scalar term4 = a2 * term3;
	std::cout << "a2 * previous_term           term4 : " << term4 << '\n';
	Scalar term5 = 5.5 * b8;
	std::cout << "5.5 * b8                     term5 : " << term5 << '\n';

	Scalar term6 = a / (2 * b);
	std::cout << "a / (2 * b)                  term6 : " << term6 << '\n';

	std::cout << "term1 + term4 + term5 + term6      : " << term1 + term4 + term5 + term6 << '\n';

	// 333.75 * b ^ 6 + a ^ 2 * (11 * a ^ 2 * b ^ 2 - b ^ 6 - 121 * b ^ 4 - 2) + 5.5 * b ^ 8 + a / (2 * b);
	return 333.75 * b6 + a2 * (11 * a2 * b2 - b6 - 121 * b4 - 2) + 5.5 * b8 + a / (2 * b);
}

// rewrite f(a,b) = 5.5 * b ^ 8 - 2 - 5.5 * b ^ 8 + a / (2 * b)
template<typename Scalar>
Scalar Rump2(double _a, double _b) {
	Scalar a, b;
	a = Scalar(_a);
	b = Scalar(_b);
	Scalar b2 = b * b;
	Scalar b4 = b2 * b2;
	Scalar b8 = b4 * b4;
	// 5.5 * b ^ 8 - 2 - 5.5 * b ^ 8 + a / (2 * b)
	return 5.5 * b8 - 2.0 - 5.5 * b8 + (a / (2 * b));
}

// f(a,b) = -2 + a / 2b
template<typename Scalar>
Scalar Rump3(double _a, double _b) {
	Scalar a, b;
	a = Scalar(_a);
	b = Scalar(_b);
	return -2.0 + a / (2 * b);
}

template<typename Scalar>
void GenerateRow(double a, double b, sw::universal::blas::matrix<double>& table, size_t rowNr) {
	table(rowNr, 0) = double(Rump1<Scalar>(a, b));
	table(rowNr, 1) = double(Rump2<Scalar>(a, b));
	table(rowNr, 2) = double(Rump3<Scalar>(a, b));
}

int main(int argc, char** argv)
try {
	using namespace std;
	using namespace sw::universal;

	cout << "Rump's equation\n";

/*
 * one off constant computation from "Handbook of Floating-Point Arithmetic":
     f(a,b) = 333.75*b^6 + a^2*(11*a^2*b^2 - b^6 - 121*b^4 - 2) + 5.5*b^8 + a/(2*b)
     for a=77617.0, b=33096.0
The exact result is -54767 / 66192 which starts with −0.8273960599...
Using pow vs repeated mutiplication for the power expressions usually yields the same answer, but not always
Running on x86 fp types we get (picking some interesting MPFR results)
Type        | Rep Mult    | Pow Func
------------+-------------+-------------
float       | -6.3383E+29 | -6.3383E+29
double      | -1.1806E+21 | -1.1806E+21
long double |  1.1726     |  5.7646E+17
quad        |  1.1726     |  1.1726
mpfr(26)    |  1.5846E+29 | -1.5846E+29
mpfr(37)    |  1.1726     | -1.5474E+26
mpfr(54)    |  1.1806e+21 |  1.1726
mpfr(76)    |  1.1726     |  1.1726
mpfr(98)    |  3.3554E+07 |  3.3554E+07
mpfr(121)   |  1.1726     |  1.1726
mpfr(122)   | -0.8274     | -0.8274
It requires 122 bits of mantissa under MPFR to get the correct answer, and is really erratic at "lower" precision.
There are a couple fpbench versions of it:  https://fpbench.org/benchmarks.html#Rump's%20example
*/

	streamsize precision = cout.precision();
	double a = 77617.0;
	double b = 33096.0;

	using Labels = std::vector<std::string>;
	using Data = blas::matrix<double>;

	Labels rowLbls = {
		"float",
		"double",
		"long double",
		"quad",
		"posit16",
		"posit32",
		"posit48",
		"posit64",
		"posit80",
		"posit128",
		"posit156",
		"integer96",
		"integer112",
		"integer128",
		"integer256",
		"bfloat16",
		"bfloat32",
		"bfloat64",
		"bfloat80",
		"bfloat100",
		"bfloat120"
	};
	Labels column = {
		"Rump1",
		"Rump2",
		"Rump3"
	};

//	using quad = bfloat<128, 11>;
	Data table(20, 5);
	GenerateRow<float>(a, b, table, 0);
	GenerateRow<double>(a, b, table, 1);
	GenerateRow<long double>(a, b, table, 2);
//	GenerateRow<quad>(a, b, table, 3);
	GenerateRow<posit<16, 2>>(a, b, table, 4);
	GenerateRow<posit<32, 2>>(a, b, table, 5);
	GenerateRow<posit<48, 2>>(a, b, table, 6);
	GenerateRow<posit<64, 2>>(a, b, table, 7);
	GenerateRow<posit<80, 2>>(a, b, table, 8);
	GenerateRow<posit<128, 2>>(a, b, table, 9);
	GenerateRow<posit<156, 2>>(a, b, table, 10);
	GenerateRow<integer<96,uint32_t>>(a, b, table, 11);
	GenerateRow<integer<112, uint32_t>>(a, b, table, 12);
	GenerateRow<integer<128, uint32_t>>(a, b, table, 13);
	GenerateRow<integer<256, uint32_t>>(a, b, table, 14);
	// print the table
	constexpr size_t COLUMN_WIDTH = 20;
	cout << setw(COLUMN_WIDTH) << "type" << "  |  ";
	for (size_t j = 0; j < size(column); ++j) {
		cout << setw(COLUMN_WIDTH) << column[j] << "  |  ";
	}
	cout << '\n';
	for (size_t i; i < size(rowLbls); ++i) {
		cout << setw(COLUMN_WIDTH) << rowLbls[i] << "  |  ";
		for (size_t j = 0; j < 3; ++j) {
			cout << setw(COLUMN_WIDTH) << table(i, j) << "  |  ";
		}
		cout << '\n';
	}
	cout << setprecision(precision);
	cout << endl;

	// trace out the original Rump equation with different number systems
	TraceRump1<double>(a, b);
	TraceRump1<posit<156,2>>(a, b);
	TraceRump1<integer<256, uint32_t>>(a, b);
	TraceRump1<decimal>(a, b);                      // TODO: some serious errors here

	return EXIT_SUCCESS;
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const std::runtime_error& err) {
	std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}
<|endoftext|>