<commit_before>#include "application.h"
#include "TimeAlarms.h"
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include "Adafruit_MCP9808.h"
#include <vector>
#undef now()

bool isDST(int dayOfMonth, int month, int dayOfWeek, String rule);
void refreshDisplayTime();
void addAlarms();
void markDelete(int hour, int minute);
int checkDelete(AlarmID_t ID);
void setAlarm(int hour, int minute, int second, bool isOnce, bool light, bool sound, int dayOfWeek); //ignore = -1, Sunday = 1, etc
void SoundAlarm();
void LightAlarm();
void lightFadeIn(int hour, int minute);

Adafruit_7segment display = Adafruit_7segment();
Adafruit_MCP9808 tempSensor = Adafruit_MCP9808();

const int ONE_DAY_MILLIS = 24 * 60 * 60 * 1000;
unsigned long lastSync = millis();
unsigned long lastBeep = millis();
int DSTJumpHour; //When DST takes effect
double temp;
bool alarm = false;

struct Preferences {
  int timeZone; //UTC time zone offset
  int hourFormat; //12 or 24
  int piezoHertz; //Piezo alarm frequency
  String DSTRule; //US, EU, or OFF
  std::vector<std::vector<int>> alarmTimes; // {hour, minute, second, isOnce, light, sound, dayOfWeek, del}
} preferences;

int piezoPin = A4;

void setup() {
  if(EEPROM.read(0) == 117) {
    EEPROM.get(1, preferences);
  } else {
    preferences = {-8, 12, 2000, "US"};
    EEPROM.put(1, preferences);
    EEPROM.put(0, 117);
  }

  if(preferences.DSTRule == "US") {
    DSTJumpHour = 2;
  } else if(preferences.DSTRule == "EU") {
    DSTJumpHour = 1 + preferences.timeZone;
  } else {
    DSTJumpHour = 0;
  }

  //Set the proper time zone according to DST status
  Time.zone(isDST(Time.day(), Time.month(), Time.weekday(), preferences.DSTRule) ? preferences.timeZone + 1 : preferences.timeZone);

  //Setup the 7-segment display
  display.begin(0x70);
  display.setBrightness(15); //TODO: adjust brightness to optimize for veneer/time

  //Add alarms stored in eeprom
  addAlarms();

  //Setup the temperature sensor
  tempSensor.setResolution(MCP9808_SLOWEST);

  //Cloud variables
  Spark.variable("Temperature", &temp, DOUBLE);
}

void loop() {
  //Request time synchronization from the Spark Cloud every 24 hours
  if (millis() - lastSync > ONE_DAY_MILLIS) {
    Spark.syncTime();
    lastSync = millis();
  }

  //Update time zone at DSTJumpHour incase DST is now in effect
  if(Time.hour() == DSTJumpHour && Time.minute() == 0) {
    Time.zone(isDST(Time.day(), Time.month(), Time.weekday(), preferences.DSTRule) ? preferences.timeZone + 1 : preferences.timeZone);
  }

  refreshDisplayTime();
  temp = tempSensor.getTemperature();

  //Sound alarm check
  if(alarm) {
    if(millis() - lastBeep > 600) {
      lastBeep = millis();
      tone(piezoPin, preferences.piezoHertz, 300);
    }
  }

  //Delay for checking alarms/timers
  Alarm.delay(1);
}

//Return true if DST is currently observed
bool isDST(int dayOfMonth, int month, int dayOfWeek, String rule) {
  if(rule == "US") {
    //Month quick check
    if (month < 3 || month > 11) {
      return false;
    } else if (month > 3 && month < 11) {
      return true;
    }
    //The US observes DST from the first Sunday of March
    //The US observes DST until the first Sunday of November
    int previousSunday = dayOfMonth - dayOfWeek + 1;
    if (month == 3) {
      return previousSunday >= 1;
    } else {
      return previousSunday <= 0;
    }
  } else if(rule == "EU") {
    //Month quick check
    if (month < 3 || month > 10) {
      return false;
    } else if (month > 3 && month < 10) {
      return true;
    }
    //The EU observes DST from the last Sunday of March
    //The EU observes DST until the last Sunday of November
    int previousSunday = dayOfMonth - dayOfWeek + 1;
    if(month == 3) {
      if(previousSunday+7>31) {
        return true;
      } else {
        return false;
      }
    } else {
      if(previousSunday+7>31) {
        return false;
      } else {
        return true;
      }
    }
  } else {
    return false;
  }
}

//display the current time on the 7-segment display
void refreshDisplayTime() {
  unsigned long currentTime = Time.now();
  int minute = Time.minute(currentTime);
  int hour;
  int digits;

  if(preferences.hourFormat == 12) {
    hour = Time.hourFormat12(currentTime);
  } else {
    hour = Time.hour(currentTime);
  }

  digits = hour/10*1000 + hour%10*100 + minute/10*10 + minute%10;

  display.print(digits);
  display.drawColon(true);
  display.writeDisplay();
}

//Add alarms from eeprom
void addAlarms() {
  for(int i=0;i<preferences.alarmTimes.size();i++) {
    setAlarm(preferences.alarmTimes.at(i).at(0), preferences.alarmTimes.at(i).at(1), preferences.alarmTimes.at(i).at(2), preferences.alarmTimes.at(3),
     preferences.alarmTimes.at(i).at(4), preferences.alarmTimes.at(i).at(5), preferences.alarmTimes.at(i).at(6));
  }
}

//Mark an alarm for deletion
void markDelete(int day, int hour, int minute) {
  for(int i=0;i<preferences.alarmTimes.size();i++) {
    if(preferences.alarmTimes.at(i).at(6) == day && preferences.alarmTimes.at(i).at(0) == hour && preferences.alarmTimes.at(i).at(1) == minute) {
      preferences.alarmTimes.at(i).at(7) = true;
    }
  }
}

//Check and if marked, delete the alarm from eeprom and TimeAlarms
//Returns 0 if deleted, -1 if not found, or the vector index
int checkDelete(AlarmID_t ID) {
  unsigned long currentTime = Time.now();
  int hour = Time.hour(currentTime);
  int minute = Time.minute(currentTime);
  int alarmIndex = -1;

  for(int i=0;i<preferences.alarmTimes.size();i++) {
    if(preferences.alarmTimes.at(i).at(0) == hour && preferences.alarmTimes.at(i).at(1) == minute) {
      alarmIndex = i;
      if(preferences.alarmTimes.at(i).at(7) == true) {
        Alarm.free(ID);
        preferences.alarmTimes.erase(i);
        return 0;
      }
    }
  }
  return alarmIndex;
}

//Set an alarm according to the given parameters
void setAlarm(int hour, int minute, int second, bool isOnce, bool light, bool sound, int dayOfWeek) {
  unsigned long currentTime = Time.now();
  int currentDay = Time.weekday(currentTime);
  int currentHour = Time.hour(currentTime);
  int currentMinute = Time.minute(currentTime);
  int day2 = dayOfWeek;
  int hour2 = hour;
  int minute2 = minute;

  //set a LightAlarm to go off 30 minutes early if light is enabled
  if(light) {
    minute2 -= 30;
    if(minute2 < 0) {
      minute2 = 60 + minute2;
      hour2 -= 1;
      if(hour2 < 0) {
        hour2 = 23;
        day2 -= 1;
        if(day2 < 1) {
          day2 = 7;
        }
      }
    }

    bool enoughTime = true;
    currentMinute -= 30;
    if(currentMinute < 0) {
      currentMinute = 60 + currentMinute;
      currentHour -= 1;
      if(currentHour < 0) {
        currentHour = 23;
        currentDay -= 1;
        if(currentDay < 1) {
          currentDay = 7;
        }
      }
    }
    if(currentDay == day2 && currentHour >= hour2 && currentMinute >= minute2) {
      enoughTime = false;
    }

    if(!enoughTime) {

    }
    if(isOnce) {
      if(!enoughTime) {
        markDelete(dayOfWeek, hour, minute);
      } else if(dayOfWeek != -1) {
        Alarm.alarmOnce(day2, hour2, minute2, second, LightAlarm);
      } else {
        Alarm.alarmOnce(hour2, minute2, second, LightAlarm);
      }
    } else {
      if(dayOfWeek != -1) {
        Alarm.alarmRepeat(day2, hour2, minute2, second, LightAlarm);
      } else {
        Alarm.alarmRepeat(hour2, minute2, second, LightAlarm);
      }
    }
  }

  //set a SoundAlarm if sound is enabled or if both sound and light are disabled
  if(sound || (!light && !sound)) {
    if(isOnce) {
      if(dayOfWeek != -1) {
        Alarm.alarmOnce(dayOfWeek, hour, minute, second, SoundAlarm);
      } else {
        Alarm.alarmOnce(hour, minute, second, SoundAlarm);
      }
    } else {
      if(dayOfWeek != -1) {
        Alarm.alarmRepeat(dayOfWeek, hour, minute, second, SoundAlarm);
      } else {
        Alarm.alarmRepeat(hour, minute, second, SoundAlarm);
      }
    }
  }

  preferences.lightAlarmTimes.push_back(hour, minute, second, isOnce, light, sound, dayOfWeek, false);
}

void SoundAlarm() {
  int alarmIndex = checkDelete(Alarm.getTriggeredAlarmId());
  if(alarmIndex == 0) {
    return;
  }
  alarm = true;
}

void LightAlarm() {
  int alarmIndex = checkDelete(Alarm.getTriggeredAlarmId());
  if(alarmIndex == 0) {
    return;
  }
  lightFadeIn(preferences.alarmTimes.at(alarmIndex).at(0), preferences.alarmTimes.at(alarmIndex).at(1));
}

void lightFadeIn(int hour, int minute) {
  //start led fade in to given time
}
<commit_msg>Light Fade in Functionality<commit_after>#include "application.h"
#include "TimeAlarms.h"
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include "Adafruit_MCP9808.h"
#include <vector>
#undef now()

bool isDST(int dayOfMonth, int month, int dayOfWeek, String rule);
void refreshDisplayTime();
void addAlarms();
void markDelete(int hour, int minute);
int checkDelete(AlarmID_t ID);
void setAlarm(int hour, int minute, int second, bool isOnce, bool light, bool sound, int dayOfWeek); //ignore = -1, Sunday = 1, etc
void SoundAlarm();
void LightAlarm();
void lightFadeIn(int hour, int minute);

Adafruit_7segment display = Adafruit_7segment();
Adafruit_MCP9808 tempSensor = Adafruit_MCP9808();

const int ONE_DAY_MILLIS = 24 * 60 * 60 * 1000;
unsigned long lastSync = millis();
unsigned long lastBeep = millis();
unsigned long lightS;
unsigned long lightF;
int maxBrightness;
int DSTJumpHour; //When DST takes effect
int driverCurrent = 1000; //Driver current output (in mA)
int maxBrightness;
double temp;
bool alarm = false;
bool light = false;

struct Preferences {
  int timeZone; //UTC time zone offset
  int hourFormat; //12 or 24
  int piezoHertz; //Piezo alarm frequency
  int LEDCurrent; //LED current rating (in mA)
  int ledBrightness; //Maximum brightness (0 to 100%)
  String DSTRule; //US, EU, or OFF
  std::vector<std::vector<int>> alarmTimes; // {hour, minute, second, isOnce, light, sound, dayOfWeek, del}
} preferences;

int piezoPin = A4;
int LEDPin = A7;

void setup() {
  if(EEPROM.read(0) == 117) {
    EEPROM.get(1, preferences);
  } else {
    std::vector<std::vector<int>> alarmTimes;
    preferences = {-8, 12, 2000, .7, 100, "US", alarmTimes};
    EEPROM.put(1, preferences);
    EEPROM.put(0, 117);
  }

  if(preferences.DSTRule == "US") {
    DSTJumpHour = 2;
  } else if(preferences.DSTRule == "EU") {
    DSTJumpHour = 1 + preferences.timeZone;
  } else {
    DSTJumpHour = 0;
  }

  //Set the proper time zone according to DST status
  Time.zone(isDST(Time.day(), Time.month(), Time.weekday(), preferences.DSTRule) ? preferences.timeZone + 1 : preferences.timeZone);

  //Setup the 7-segment display
  display.begin(0x70);
  display.setBrightness(15); //TODO: adjust brightness to optimize for veneer/time

  //Pin setup
  pinMode(LEDPin, OUTPUT);

  //Add alarms stored in eeprom
  addAlarms();

  //Setup the temperature sensor
  tempSensor.setResolution(MCP9808_SLOWEST);

  //Cloud variables
  Spark.variable("Temperature", &temp, DOUBLE);
}

void loop() {
  unsigned long currentTime = Time.now();

  //Request time synchronization from the Spark Cloud every 24 hours
  if (millis() - lastSync > ONE_DAY_MILLIS) {
    Spark.syncTime();
    lastSync = millis();
  }

  //Update time zone at DSTJumpHour incase DST is now in effect
  if(Time.hour(currentTime) == DSTJumpHour && Time.minute(currentTime) == 0) {
    Time.zone(isDST(Time.day(currentTime), Time.month(currentTime), Time.weekday(currentTime), preferences.DSTRule) ? preferences.timeZone + 1 : preferences.timeZone);
  }

  refreshDisplayTime();
  temp = tempSensor.getTemperature();

  //Sound alarm check
  if(alarm) {
    if(millis() - lastBeep > 600) {
      lastBeep = millis();
      tone(piezoPin, preferences.piezoHertz, 300);
    }
  }

  //Light alarm check
  if(light) {
    analogWrite(LEDPin, min(maxBrightness, ((currentTime - lightS) / (lightF - lightS)) * maxBrightness));
  }

  //Delay for checking alarms/timers
  Alarm.delay(1);
}

//Return true if DST is currently observed
bool isDST(int dayOfMonth, int month, int dayOfWeek, String rule) {
  if(rule == "US") {
    //Month quick check
    if (month < 3 || month > 11) {
      return false;
    } else if (month > 3 && month < 11) {
      return true;
    }
    //The US observes DST from the first Sunday of March
    //The US observes DST until the first Sunday of November
    int previousSunday = dayOfMonth - dayOfWeek + 1;
    if (month == 3) {
      return previousSunday >= 1;
    } else {
      return previousSunday <= 0;
    }
  } else if(rule == "EU") {
    //Month quick check
    if (month < 3 || month > 10) {
      return false;
    } else if (month > 3 && month < 10) {
      return true;
    }
    //The EU observes DST from the last Sunday of March
    //The EU observes DST until the last Sunday of November
    int previousSunday = dayOfMonth - dayOfWeek + 1;
    if(month == 3) {
      if(previousSunday+7>31) {
        return true;
      } else {
        return false;
      }
    } else {
      if(previousSunday+7>31) {
        return false;
      } else {
        return true;
      }
    }
  } else {
    return false;
  }
}

//display the current time on the 7-segment display
void refreshDisplayTime() {
  unsigned long currentTime = Time.now();
  int minute = Time.minute(currentTime);
  int hour;
  int digits;

  if(preferences.hourFormat == 12) {
    hour = Time.hourFormat12(currentTime);
  } else {
    hour = Time.hour(currentTime);
  }

  digits = hour/10*1000 + hour%10*100 + minute/10*10 + minute%10;

  display.print(digits);
  display.drawColon(true);
  display.writeDisplay();
}

//Add alarms from eeprom
void addAlarms() {
  for(int i=0;i<preferences.alarmTimes.size();i++) {
    setAlarm(preferences.alarmTimes.at(i).at(0), preferences.alarmTimes.at(i).at(1), preferences.alarmTimes.at(i).at(2), preferences.alarmTimes.at(3),
     preferences.alarmTimes.at(i).at(4), preferences.alarmTimes.at(i).at(5), preferences.alarmTimes.at(i).at(6));
  }
}

//Mark an alarm for deletion
void markDelete(int day, int hour, int minute) {
  for(int i=0;i<preferences.alarmTimes.size();i++) {
    if(preferences.alarmTimes.at(i).at(6) == day && preferences.alarmTimes.at(i).at(0) == hour && preferences.alarmTimes.at(i).at(1) == minute) {
      preferences.alarmTimes.at(i).at(7) = true;
    }
  }
}

//Check and if marked, delete the alarm from eeprom and TimeAlarms
//Returns 0 if deleted, -1 if not found, or the vector index
int checkDelete(AlarmID_t ID) {
  unsigned long currentTime = Time.now();
  int hour = Time.hour(currentTime);
  int minute = Time.minute(currentTime);
  int alarmIndex = -1;

  for(int i=0;i<preferences.alarmTimes.size();i++) {
    if(preferences.alarmTimes.at(i).at(0) == hour && preferences.alarmTimes.at(i).at(1) == minute) {
      alarmIndex = i;
      if(preferences.alarmTimes.at(i).at(7) == true) {
        Alarm.free(ID);
        preferences.alarmTimes.erase(i);
        return 0;
      }
    }
  }
  return alarmIndex;
}

//Set an alarm according to the given parameters
void setAlarm(int hour, int minute, int second, bool isOnce, bool light, bool sound, int dayOfWeek) {
  unsigned long currentTime = Time.now();
  int currentDay = Time.weekday(currentTime);
  int currentHour = Time.hour(currentTime);
  int currentMinute = Time.minute(currentTime);
  int day2 = dayOfWeek;
  int hour2 = hour;
  int minute2 = minute;

  //set a LightAlarm to go off 30 minutes early if light is enabled
  if(light) {
    minute2 -= 30;
    if(minute2 < 0) {
      minute2 = 60 + minute2;
      hour2 -= 1;
      if(hour2 < 0) {
        hour2 = 23;
        day2 -= 1;
        if(day2 < 1) {
          day2 = 7;
        }
      }
    }

    bool enoughTime = true;
    currentMinute -= 30;
    if(currentMinute < 0) {
      currentMinute = 60 + currentMinute;
      currentHour -= 1;
      if(currentHour < 0) {
        currentHour = 23;
        currentDay -= 1;
        if(currentDay < 1) {
          currentDay = 7;
        }
      }
    }
    if(currentDay == day2 && currentHour >= hour2 && currentMinute >= minute2) {
      enoughTime = false;
    }

    if(!enoughTime) {

    }
    if(isOnce) {
      if(!enoughTime) {
        markDelete(dayOfWeek, hour, minute);
      } else if(dayOfWeek != -1) {
        Alarm.alarmOnce(day2, hour2, minute2, second, LightAlarm);
      } else {
        Alarm.alarmOnce(hour2, minute2, second, LightAlarm);
      }
    } else {
      if(dayOfWeek != -1) {
        Alarm.alarmRepeat(day2, hour2, minute2, second, LightAlarm);
      } else {
        Alarm.alarmRepeat(hour2, minute2, second, LightAlarm);
      }
    }
  }

  //set a SoundAlarm if sound is enabled or if both sound and light are disabled
  if(sound || (!light && !sound)) {
    if(isOnce) {
      if(dayOfWeek != -1) {
        Alarm.alarmOnce(dayOfWeek, hour, minute, second, SoundAlarm);
      } else {
        Alarm.alarmOnce(hour, minute, second, SoundAlarm);
      }
    } else {
      if(dayOfWeek != -1) {
        Alarm.alarmRepeat(dayOfWeek, hour, minute, second, SoundAlarm);
      } else {
        Alarm.alarmRepeat(hour, minute, second, SoundAlarm);
      }
    }
  }

  preferences.lightAlarmTimes.push_back(hour, minute, second, isOnce, light, sound, dayOfWeek, false);
}

void SoundAlarm() {
  int alarmIndex = checkDelete(Alarm.getTriggeredAlarmId());
  if(alarmIndex == 0) {
    return;
  }
  alarm = true;
}

void LightAlarm() {
  int alarmIndex = checkDelete(Alarm.getTriggeredAlarmId());
  if(alarmIndex == 0) {
    return;
  }
  lightFadeIn(preferences.alarmTimes.at(alarmIndex).at(0), preferences.alarmTimes.at(alarmIndex).at(1));
}

//Start light fade in to a given time
void lightFadeIn(int hour, int minute) {
  lightS = Time.now();
  int difference = (hour*60 + minute) - (Time.hour(lightS)*60 + Time.minute(lightS));
  if(difference < 0) {
    difference += 24*60;
  }
  lightF = lightS + difference*60;
  maxBrightness = 4095*(preferences.LEDCurrent/driverCurrent)*(preferences.ledBrightness/100);
  light = true;
}
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkDashPathEffect.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkRandom.h"
#include "SkString.h"
#include "SkTDArray.h"

/*
 *  Cases to consider:
 *
 *  1. antialiasing on/off (esp. width <= 1)
 *  2. strokewidth == 0, 1, 2
 *  3. hline, vline, diagonal, rect, oval
 *  4. dots [1,1] ([N,N] where N=strokeWidth?) or arbitrary (e.g. [2,1] or [1,2,3,2])
 */
static void path_hline(SkPath* path) {
    path->moveTo(SkIntToScalar(10), SkIntToScalar(10));
    path->lineTo(SkIntToScalar(600), SkIntToScalar(10));
}

class DashBench : public SkBenchmark {
protected:
    SkString            fName;
    SkTDArray<SkScalar> fIntervals;
    int                 fWidth;
    SkPoint             fPts[2];
    bool                fDoClip;

    enum {
        N = SkBENCHLOOP(100)
    };
public:
    DashBench(void* param, const SkScalar intervals[], int count, int width,
              bool doClip = false) : INHERITED(param) {
        fIntervals.append(count, intervals);
        for (int i = 0; i < count; ++i) {
            fIntervals[i] *= width;
        }
        fWidth = width;
        fName.printf("dash_%d_%s", width, doClip ? "clipped" : "noclip");
        fDoClip = doClip;

        fPts[0].set(SkIntToScalar(10), SkIntToScalar(10));
        fPts[1].set(SkIntToScalar(600), SkIntToScalar(10));
    }

    virtual void makePath(SkPath* path) {
        path_hline(path);
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPaint paint;
        this->setupPaint(&paint);
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(SkIntToScalar(fWidth));
        paint.setAntiAlias(false);

        SkPath path;
        this->makePath(&path);

        paint.setPathEffect(new SkDashPathEffect(fIntervals.begin(),
                                                 fIntervals.count(), 0))->unref();

        if (fDoClip) {
            SkRect r = path.getBounds();
            r.inset(-SkIntToScalar(20), -SkIntToScalar(20));
            // now move it so we don't intersect
            r.offset(0, r.height() * 3 / 2);
            canvas->clipRect(r);
        }

        this->handlePath(canvas, path, paint, N);
    }

    virtual void handlePath(SkCanvas* canvas, const SkPath& path,
                            const SkPaint& paint, int N) {
        for (int i = 0; i < N; ++i) {
//            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint);
            canvas->drawPath(path, paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class RectDashBench : public DashBench {
public:
    RectDashBench(void* param, const SkScalar intervals[], int count, int width, bool doClip = false)
    : INHERITED(param, intervals, count, width) {
        fName.append("_rect");
    }

protected:
    virtual void handlePath(SkCanvas* canvas, const SkPath& path,
                            const SkPaint& paint, int N) SK_OVERRIDE {
        SkPoint pts[2];
        if (!path.isLine(pts) || pts[0].fY != pts[1].fY) {
            this->INHERITED::handlePath(canvas, path, paint, N);
        } else {
            SkRect rect;
            rect.fLeft = pts[0].fX;
            rect.fTop = pts[0].fY - paint.getStrokeWidth() / 2;
            rect.fRight = rect.fLeft + SkIntToScalar(fWidth);
            rect.fBottom = rect.fTop + paint.getStrokeWidth();

            SkPaint p(paint);
            p.setStyle(SkPaint::kFill_Style);
            p.setPathEffect(NULL);

            int count = SkScalarRoundToInt((pts[1].fX - pts[0].fX) / (2*fWidth));
            SkScalar dx = SkIntToScalar(2 * fWidth);

            for (int i = 0; i < N*10; ++i) {
                SkRect r = rect;
                for (int j = 0; j < count; ++j) {
                    canvas->drawRect(r, p);
                    r.offset(dx, 0);
                }
            }
        }
    }

private:
    typedef DashBench INHERITED;
};

static void make_unit_star(SkPath* path, int n) {
    SkScalar rad = -SK_ScalarPI / 2;
    const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;

    path->moveTo(0, -SK_Scalar1);
    for (int i = 1; i < n; i++) {
        rad += drad;
        SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
        path->lineTo(cosV, sinV);
    }
    path->close();
}

static void make_poly(SkPath* path) {
    make_unit_star(path, 9);
    SkMatrix matrix;
    matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
    path->transform(matrix);
}

static void make_quad(SkPath* path) {
    SkScalar x0 = SkIntToScalar(10);
    SkScalar y0 = SkIntToScalar(10);
    path->moveTo(x0, y0);
    path->quadTo(x0,                    y0 + 400 * SK_Scalar1,
                 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1);
}

static void make_cubic(SkPath* path) {
    SkScalar x0 = SkIntToScalar(10);
    SkScalar y0 = SkIntToScalar(10);
    path->moveTo(x0, y0);
    path->cubicTo(x0,                    y0 + 400 * SK_Scalar1,
                  x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1,
                  x0 + 600 * SK_Scalar1, y0);
}

class MakeDashBench : public SkBenchmark {
    SkString fName;
    SkPath   fPath;
    SkAutoTUnref<SkPathEffect> fPE;

    enum {
        N = SkBENCHLOOP(400)
    };

public:
    MakeDashBench(void* param, void (*proc)(SkPath*), const char name[]) : INHERITED(param) {
        fName.printf("makedash_%s", name);
        proc(&fPath);

        SkScalar vals[] = { SkIntToScalar(4), SkIntToScalar(4) };
        fPE.reset(new SkDashPathEffect(vals, 2, 0));
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPath dst;
        for (int i = 0; i < N; ++i) {
            SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);

            fPE->filterPath(&dst, fPath, &rec);
            dst.rewind();
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

/*
 *  We try to special case square dashes (intervals are equal to strokewidth).
 */
class DashLineBench : public SkBenchmark {
    SkString fName;
    SkScalar fStrokeWidth;
    bool     fIsRound;
    SkAutoTUnref<SkPathEffect> fPE;

    enum {
        N = SkBENCHLOOP(200)
    };

public:
    DashLineBench(void* param, SkScalar width, bool isRound) : INHERITED(param) {
        fName.printf("dashline_%g_%s", SkScalarToFloat(width), isRound ? "circle" : "square");
        fStrokeWidth = width;
        fIsRound = isRound;

        SkScalar vals[] = { SK_Scalar1, SK_Scalar1 };
        fPE.reset(new SkDashPathEffect(vals, 2, 0));
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPaint paint;
        this->setupPaint(&paint);
        paint.setStrokeWidth(fStrokeWidth);
        paint.setStrokeCap(fIsRound ? SkPaint::kRound_Cap : SkPaint::kSquare_Cap);
        paint.setPathEffect(fPE);
        for (int i = 0; i < N; ++i) {
            canvas->drawLine(10 * SK_Scalar1, 10 * SK_Scalar1,
                             640 * SK_Scalar1, 10 * SK_Scalar1, paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class DrawPointsDashingBench : public SkBenchmark {
    SkString fName;
    int      fStrokeWidth;
    bool     fDoAA;

    SkAutoTUnref<SkPathEffect> fPathEffect;

    enum {
        N = SkBENCHLOOP(480)
    };

public:
    DrawPointsDashingBench(void* param, int dashLength, int strokeWidth, bool doAA)
        : INHERITED(param) {
        fName.printf("drawpointsdash_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw");
        fStrokeWidth = strokeWidth;
        fDoAA = doAA;

        SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) };
        fPathEffect.reset(new SkDashPathEffect(vals, 2, SK_Scalar1, false));
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {

        SkPaint p;
        this->setupPaint(&p);
        p.setColor(SK_ColorBLACK);
        p.setStyle(SkPaint::kStroke_Style);
        p.setStrokeWidth(SkIntToScalar(fStrokeWidth));
        p.setPathEffect(fPathEffect);
        p.setAntiAlias(fDoAA);

        SkPoint pts[2] = {
            { SkIntToScalar(10), 0 },
            { SkIntToScalar(640), 0 }
        };

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

            pts[0].fY = pts[1].fY = SkIntToScalar(i % 480);
            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class GiantDashBench : public SkBenchmark {
    SkString fName;
    SkPoint  fPts[2];
    SkAutoTUnref<SkPathEffect> fPathEffect;

    enum {
        N = SkBENCHLOOP(10)
    };

public:
    enum LineType {
        kHori_LineType,
        kVert_LineType,
        kDiag_LineType
    };

    static const char* LineTypeName(LineType lt) {
        static const char* gNames[] = { "hori", "vert", "diag" };
        SkASSERT((size_t)lt <= SK_ARRAY_COUNT(gNames));
        return gNames[lt];
    }

    GiantDashBench(void* param, LineType lt) : INHERITED(param) {
        fName.printf("giantdashline_%s", LineTypeName(lt));

        // deliberately pick intervals that won't be caught by asPoints(), so
        // we can test the filterPath code-path.
        const SkScalar intervals[] = { 2, 1 };
        fPathEffect.reset(new SkDashPathEffect(intervals,
                                               SK_ARRAY_COUNT(intervals), 0));

        SkScalar cx = 640 / 2;  // center X
        SkScalar cy = 480 / 2;  // center Y
        SkMatrix matrix;

        switch (lt) {
            case kHori_LineType:
                matrix.setIdentity();
                break;
            case kVert_LineType:
                matrix.setRotate(90, cx, cy);
                break;
            case kDiag_LineType:
                matrix.setRotate(45, cx, cy);
                break;
        }

        const SkScalar overshoot = 10*1000;
        const SkPoint pts[2] = {
            { -overshoot, cy }, { 640 + overshoot, cy }
        };
        matrix.mapPoints(fPts, pts, 2);
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPaint p;
        this->setupPaint(&p);
        p.setStyle(SkPaint::kStroke_Style);
        p.setStrokeWidth(1);
        p.setPathEffect(fPathEffect);

        for (int i = 0; i < N; ++i) {
            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 };

#define PARAM(array)    array, SK_ARRAY_COUNT(array)

DEF_BENCH( return new DashBench(p, PARAM(gDots), 0); )
DEF_BENCH( return new DashBench(p, PARAM(gDots), 1); )
DEF_BENCH( return new DashBench(p, PARAM(gDots), 1, true); )
DEF_BENCH( return new DashBench(p, PARAM(gDots), 4); )
DEF_BENCH( return new MakeDashBench(p, make_poly, "poly"); )
DEF_BENCH( return new MakeDashBench(p, make_quad, "quad"); )
DEF_BENCH( return new MakeDashBench(p, make_cubic, "cubic"); )
DEF_BENCH( return new DashLineBench(p, 0, false); )
DEF_BENCH( return new DashLineBench(p, SK_Scalar1, false); )
DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, false); )
DEF_BENCH( return new DashLineBench(p, 0, true); )
DEF_BENCH( return new DashLineBench(p, SK_Scalar1, true); )
DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, true); )

DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, false); )
DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, true); )
DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, false); )
DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, true); )
DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, false); )
DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, true); )

DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType); )
DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType); )
DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType); )
<commit_msg>update to emphasis the culling aspect of the test (making the line much longer, and adding hairline and non-hairline cases.)<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkDashPathEffect.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkRandom.h"
#include "SkString.h"
#include "SkTDArray.h"

/*
 *  Cases to consider:
 *
 *  1. antialiasing on/off (esp. width <= 1)
 *  2. strokewidth == 0, 1, 2
 *  3. hline, vline, diagonal, rect, oval
 *  4. dots [1,1] ([N,N] where N=strokeWidth?) or arbitrary (e.g. [2,1] or [1,2,3,2])
 */
static void path_hline(SkPath* path) {
    path->moveTo(SkIntToScalar(10), SkIntToScalar(10));
    path->lineTo(SkIntToScalar(600), SkIntToScalar(10));
}

class DashBench : public SkBenchmark {
protected:
    SkString            fName;
    SkTDArray<SkScalar> fIntervals;
    int                 fWidth;
    SkPoint             fPts[2];
    bool                fDoClip;

    enum {
        N = SkBENCHLOOP(100)
    };
public:
    DashBench(void* param, const SkScalar intervals[], int count, int width,
              bool doClip = false) : INHERITED(param) {
        fIntervals.append(count, intervals);
        for (int i = 0; i < count; ++i) {
            fIntervals[i] *= width;
        }
        fWidth = width;
        fName.printf("dash_%d_%s", width, doClip ? "clipped" : "noclip");
        fDoClip = doClip;

        fPts[0].set(SkIntToScalar(10), SkIntToScalar(10));
        fPts[1].set(SkIntToScalar(600), SkIntToScalar(10));
    }

    virtual void makePath(SkPath* path) {
        path_hline(path);
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPaint paint;
        this->setupPaint(&paint);
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(SkIntToScalar(fWidth));
        paint.setAntiAlias(false);

        SkPath path;
        this->makePath(&path);

        paint.setPathEffect(new SkDashPathEffect(fIntervals.begin(),
                                                 fIntervals.count(), 0))->unref();

        if (fDoClip) {
            SkRect r = path.getBounds();
            r.inset(-SkIntToScalar(20), -SkIntToScalar(20));
            // now move it so we don't intersect
            r.offset(0, r.height() * 3 / 2);
            canvas->clipRect(r);
        }

        this->handlePath(canvas, path, paint, N);
    }

    virtual void handlePath(SkCanvas* canvas, const SkPath& path,
                            const SkPaint& paint, int N) {
        for (int i = 0; i < N; ++i) {
//            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint);
            canvas->drawPath(path, paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class RectDashBench : public DashBench {
public:
    RectDashBench(void* param, const SkScalar intervals[], int count, int width, bool doClip = false)
    : INHERITED(param, intervals, count, width) {
        fName.append("_rect");
    }

protected:
    virtual void handlePath(SkCanvas* canvas, const SkPath& path,
                            const SkPaint& paint, int N) SK_OVERRIDE {
        SkPoint pts[2];
        if (!path.isLine(pts) || pts[0].fY != pts[1].fY) {
            this->INHERITED::handlePath(canvas, path, paint, N);
        } else {
            SkRect rect;
            rect.fLeft = pts[0].fX;
            rect.fTop = pts[0].fY - paint.getStrokeWidth() / 2;
            rect.fRight = rect.fLeft + SkIntToScalar(fWidth);
            rect.fBottom = rect.fTop + paint.getStrokeWidth();

            SkPaint p(paint);
            p.setStyle(SkPaint::kFill_Style);
            p.setPathEffect(NULL);

            int count = SkScalarRoundToInt((pts[1].fX - pts[0].fX) / (2*fWidth));
            SkScalar dx = SkIntToScalar(2 * fWidth);

            for (int i = 0; i < N*10; ++i) {
                SkRect r = rect;
                for (int j = 0; j < count; ++j) {
                    canvas->drawRect(r, p);
                    r.offset(dx, 0);
                }
            }
        }
    }

private:
    typedef DashBench INHERITED;
};

static void make_unit_star(SkPath* path, int n) {
    SkScalar rad = -SK_ScalarPI / 2;
    const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;

    path->moveTo(0, -SK_Scalar1);
    for (int i = 1; i < n; i++) {
        rad += drad;
        SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
        path->lineTo(cosV, sinV);
    }
    path->close();
}

static void make_poly(SkPath* path) {
    make_unit_star(path, 9);
    SkMatrix matrix;
    matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
    path->transform(matrix);
}

static void make_quad(SkPath* path) {
    SkScalar x0 = SkIntToScalar(10);
    SkScalar y0 = SkIntToScalar(10);
    path->moveTo(x0, y0);
    path->quadTo(x0,                    y0 + 400 * SK_Scalar1,
                 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1);
}

static void make_cubic(SkPath* path) {
    SkScalar x0 = SkIntToScalar(10);
    SkScalar y0 = SkIntToScalar(10);
    path->moveTo(x0, y0);
    path->cubicTo(x0,                    y0 + 400 * SK_Scalar1,
                  x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1,
                  x0 + 600 * SK_Scalar1, y0);
}

class MakeDashBench : public SkBenchmark {
    SkString fName;
    SkPath   fPath;
    SkAutoTUnref<SkPathEffect> fPE;

    enum {
        N = SkBENCHLOOP(400)
    };

public:
    MakeDashBench(void* param, void (*proc)(SkPath*), const char name[]) : INHERITED(param) {
        fName.printf("makedash_%s", name);
        proc(&fPath);

        SkScalar vals[] = { SkIntToScalar(4), SkIntToScalar(4) };
        fPE.reset(new SkDashPathEffect(vals, 2, 0));
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPath dst;
        for (int i = 0; i < N; ++i) {
            SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);

            fPE->filterPath(&dst, fPath, &rec);
            dst.rewind();
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

/*
 *  We try to special case square dashes (intervals are equal to strokewidth).
 */
class DashLineBench : public SkBenchmark {
    SkString fName;
    SkScalar fStrokeWidth;
    bool     fIsRound;
    SkAutoTUnref<SkPathEffect> fPE;

    enum {
        N = SkBENCHLOOP(200)
    };

public:
    DashLineBench(void* param, SkScalar width, bool isRound) : INHERITED(param) {
        fName.printf("dashline_%g_%s", SkScalarToFloat(width), isRound ? "circle" : "square");
        fStrokeWidth = width;
        fIsRound = isRound;

        SkScalar vals[] = { SK_Scalar1, SK_Scalar1 };
        fPE.reset(new SkDashPathEffect(vals, 2, 0));
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPaint paint;
        this->setupPaint(&paint);
        paint.setStrokeWidth(fStrokeWidth);
        paint.setStrokeCap(fIsRound ? SkPaint::kRound_Cap : SkPaint::kSquare_Cap);
        paint.setPathEffect(fPE);
        for (int i = 0; i < N; ++i) {
            canvas->drawLine(10 * SK_Scalar1, 10 * SK_Scalar1,
                             640 * SK_Scalar1, 10 * SK_Scalar1, paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class DrawPointsDashingBench : public SkBenchmark {
    SkString fName;
    int      fStrokeWidth;
    bool     fDoAA;

    SkAutoTUnref<SkPathEffect> fPathEffect;

    enum {
        N = SkBENCHLOOP(480)
    };

public:
    DrawPointsDashingBench(void* param, int dashLength, int strokeWidth, bool doAA)
        : INHERITED(param) {
        fName.printf("drawpointsdash_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw");
        fStrokeWidth = strokeWidth;
        fDoAA = doAA;

        SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) };
        fPathEffect.reset(new SkDashPathEffect(vals, 2, SK_Scalar1, false));
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {

        SkPaint p;
        this->setupPaint(&p);
        p.setColor(SK_ColorBLACK);
        p.setStyle(SkPaint::kStroke_Style);
        p.setStrokeWidth(SkIntToScalar(fStrokeWidth));
        p.setPathEffect(fPathEffect);
        p.setAntiAlias(fDoAA);

        SkPoint pts[2] = {
            { SkIntToScalar(10), 0 },
            { SkIntToScalar(640), 0 }
        };

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

            pts[0].fY = pts[1].fY = SkIntToScalar(i % 480);
            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

// Want to test how we handle dashing when 99% of the dash is clipped out
class GiantDashBench : public SkBenchmark {
    SkString fName;
    SkScalar fStrokeWidth;
    SkPoint  fPts[2];
    SkAutoTUnref<SkPathEffect> fPathEffect;

public:
    enum LineType {
        kHori_LineType,
        kVert_LineType,
        kDiag_LineType
    };

    static const char* LineTypeName(LineType lt) {
        static const char* gNames[] = { "hori", "vert", "diag" };
        SkASSERT((size_t)lt <= SK_ARRAY_COUNT(gNames));
        return gNames[lt];
    }

    GiantDashBench(void* param, LineType lt, SkScalar width) : INHERITED(param) {
        fName.printf("giantdashline_%s_%g", LineTypeName(lt), width);
        fStrokeWidth = width;

        // deliberately pick intervals that won't be caught by asPoints(), so
        // we can test the filterPath code-path.
        const SkScalar intervals[] = { 2, 1 };
        fPathEffect.reset(new SkDashPathEffect(intervals,
                                               SK_ARRAY_COUNT(intervals), 0));

        SkScalar cx = 640 / 2;  // center X
        SkScalar cy = 480 / 2;  // center Y
        SkMatrix matrix;

        switch (lt) {
            case kHori_LineType:
                matrix.setIdentity();
                break;
            case kVert_LineType:
                matrix.setRotate(90, cx, cy);
                break;
            case kDiag_LineType:
                matrix.setRotate(45, cx, cy);
                break;
        }

        const SkScalar overshoot = 100*1000;
        const SkPoint pts[2] = {
            { -overshoot, cy }, { 640 + overshoot, cy }
        };
        matrix.mapPoints(fPts, pts, 2);
    }

protected:
    virtual const char* onGetName() SK_OVERRIDE {
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        SkPaint p;
        this->setupPaint(&p);
        p.setStyle(SkPaint::kStroke_Style);
        p.setStrokeWidth(fStrokeWidth);
        p.setPathEffect(fPathEffect);

        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p);
    }

private:
    typedef SkBenchmark INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 };

#define PARAM(array)    array, SK_ARRAY_COUNT(array)

DEF_BENCH( return new DashBench(p, PARAM(gDots), 0); )
DEF_BENCH( return new DashBench(p, PARAM(gDots), 1); )
DEF_BENCH( return new DashBench(p, PARAM(gDots), 1, true); )
DEF_BENCH( return new DashBench(p, PARAM(gDots), 4); )
DEF_BENCH( return new MakeDashBench(p, make_poly, "poly"); )
DEF_BENCH( return new MakeDashBench(p, make_quad, "quad"); )
DEF_BENCH( return new MakeDashBench(p, make_cubic, "cubic"); )
DEF_BENCH( return new DashLineBench(p, 0, false); )
DEF_BENCH( return new DashLineBench(p, SK_Scalar1, false); )
DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, false); )
DEF_BENCH( return new DashLineBench(p, 0, true); )
DEF_BENCH( return new DashLineBench(p, SK_Scalar1, true); )
DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, true); )

DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, false); )
DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, true); )
DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, false); )
DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, true); )
DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, false); )
DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, true); )

DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 0); )
DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 0); )
DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 0); )

// pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing

// hori_2 is just too slow to enable at the moment
//DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 2); )
DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 2); )
DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 2); )
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <ctype.h>

#include "Benchmark.h"
#include "CrashHandler.h"
#include "Stats.h"
#include "Timer.h"

#include "SkCanvas.h"
#include "SkCommandLineFlags.h"
#include "SkForceLinking.h"
#include "SkGraphics.h"
#include "SkString.h"
#include "SkSurface.h"

#if SK_SUPPORT_GPU
    #include "GrContextFactory.h"
    GrContextFactory gGrFactory;
#endif

__SK_FORCE_IMAGE_DECODER_LINKING;

DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
DEFINE_double(overheadGoal, 0.0001,
              "Loop until timer overhead is at most this fraction of our measurments.");
DEFINE_string(match, "", "The usual filters on file names of benchmarks to measure.");
DEFINE_bool2(quiet, q, false, "Print only bench name and minimum sample.");
DEFINE_bool2(verbose, v, false, "Print all samples.");
DEFINE_string(config, "nonrendering 8888 gpu", "Configs to measure. Options: "
              "565 8888 gpu nonrendering debug nullgpu msaa4 msaa16 nvprmsaa4 nvprmsaa16 angle");
DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");

static SkString humanize(double ms) {
    if (ms > 1e+3) return SkStringPrintf("%.3gs",  ms/1e3);
    if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
    if (ms < 1)    return SkStringPrintf("%.3gµs", ms*1e3);
    return SkStringPrintf("%.3gms", ms);
}

static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) {
    WallTimer timer;
    timer.start();
    if (bench) {
        bench->draw(loops, canvas);
    }
    if (canvas) {
        canvas->flush();
    }
#if SK_SUPPORT_GPU
    if (gl) {
        SK_GL(*gl, Flush());
        gl->swapBuffers();
    }
#endif
    timer.end();
    return timer.fWall;
}

static double estimate_timer_overhead() {
    double overhead = 0;
    for (int i = 0; i < FLAGS_overheadLoops; i++) {
        overhead += time(1, NULL, NULL, NULL);
    }
    return overhead / FLAGS_overheadLoops;
}

static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
    // First figure out approximately how many loops of bench it takes to make overhead negligible.
    double bench_plus_overhead;
    do {
        bench_plus_overhead = time(1, bench, canvas, NULL);
    } while (bench_plus_overhead < overhead);  // Shouldn't normally happen.

    // Later we'll just start and stop the timer once but loop N times.
    // We'll pick N to make timer overhead negligible:
    //
    //          overhead
    //  -------------------------  < FLAGS_overheadGoal
    //  overhead + N * Bench Time
    //
    // where bench_plus_overhead ≈ overhead + Bench Time.
    //
    // Doing some math, we get:
    //
    //  (overhead / FLAGS_overheadGoal) - overhead
    //  ------------------------------------------  < N
    //       bench_plus_overhead - overhead)
    //
    // Luckily, this also works well in practice. :)
    const double numer = overhead / FLAGS_overheadGoal - overhead;
    const double denom = bench_plus_overhead - overhead;
    const int loops = (int)ceil(numer / denom);

    for (int i = 0; i < FLAGS_samples; i++) {
        samples[i] = time(loops, bench, canvas, NULL) / loops;
    }
    return loops;
}

#if SK_SUPPORT_GPU
static int gpu_bench(SkGLContextHelper* gl,
                     Benchmark* bench,
                     SkCanvas* canvas,
                     double* samples) {
    // Make sure we're done with whatever came before.
    SK_GL(*gl, Finish);

    // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
    int loops = 1;
    double elapsed = 0;
    do {
        loops *= 2;
        // If the GPU lets frames lag at all, we need to make sure we're timing
        // _this_ round, not still timing last round.  We force this by looping
        // more times than any reasonable GPU will allow frames to lag.
        for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
            elapsed = time(loops, bench, canvas, gl);
        }
    } while (elapsed < FLAGS_gpuMs);

    // We've overshot at least a little.  Scale back linearly.
    loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);

    // Might as well make sure we're not still timing our calibration.
    SK_GL(*gl, Finish);

    // Pretty much the same deal as the calibration: do some warmup to make
    // sure we're timing steady-state pipelined frames.
    for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
        time(loops, bench, canvas, gl);
    }

    // Now, actually do the timing!
    for (int i = 0; i < FLAGS_samples; i++) {
        samples[i] = time(loops, bench, canvas, gl) / loops;
    }
    return loops;
}
#endif

static SkString to_lower(const char* str) {
    SkString lower(str);
    for (size_t i = 0; i < lower.size(); i++) {
        lower[i] = tolower(lower[i]);
    }
    return lower;
}

struct Target {
    const char* config;
    Benchmark::Backend backend;
    SkAutoTDelete<SkSurface> surface;
#if SK_SUPPORT_GPU
    SkGLContextHelper* gl;
#endif
};

// If bench is enabled for backend/config, returns a Target* for them, otherwise NULL.
static Target* is_enabled(Benchmark* bench, Benchmark::Backend backend, const char* config) {
    if (!bench->isSuitableFor(backend)) {
        return NULL;
    }

    for (int i = 0; i < FLAGS_config.count(); i++) {
        if (to_lower(FLAGS_config[i]).equals(config)) {
            Target* target = new Target;
            target->config  = config;
            target->backend = backend;
            return target;
        }
    }
    return NULL;
}

// Append all targets that are suitable for bench.
static void create_targets(Benchmark* bench, SkTDArray<Target*>* targets) {
    const int w = bench->getSize().fX,
              h = bench->getSize().fY;
    const SkImageInfo _8888 = { w, h, kN32_SkColorType,     kPremul_SkAlphaType },
                       _565 = { w, h, kRGB_565_SkColorType, kOpaque_SkAlphaType };

    #define CPU_TARGET(config, backend, code)                              \
        if (Target* t = is_enabled(bench, Benchmark::backend, #config)) {  \
            t->surface.reset(code);                                        \
            targets->push(t);                                              \
        }
    CPU_TARGET(nonrendering, kNonRendering_Backend, NULL)
    CPU_TARGET(8888, kRaster_Backend, SkSurface::NewRaster(_8888))
    CPU_TARGET(565,  kRaster_Backend, SkSurface::NewRaster(_565))

#if SK_SUPPORT_GPU
    #define GPU_TARGET(config, ctxType, info, samples)                                            \
        if (Target* t = is_enabled(bench, Benchmark::kGPU_Backend, #config)) {                    \
            t->surface.reset(SkSurface::NewRenderTarget(gGrFactory.get(ctxType), info, samples)); \
            t->gl = gGrFactory.getGLContext(ctxType);                                             \
            targets->push(t);                                                                     \
        }
    GPU_TARGET(gpu,      GrContextFactory::kNative_GLContextType, _8888, 0)
    GPU_TARGET(msaa4,    GrContextFactory::kNative_GLContextType, _8888, 4)
    GPU_TARGET(msaa16,   GrContextFactory::kNative_GLContextType, _8888, 16)
    GPU_TARGET(nvprmsaa4,  GrContextFactory::kNVPR_GLContextType, _8888, 4)
    GPU_TARGET(nvprmsaa16, GrContextFactory::kNVPR_GLContextType, _8888, 16)
    GPU_TARGET(debug,     GrContextFactory::kDebug_GLContextType, _8888, 0)
    GPU_TARGET(nullgpu,    GrContextFactory::kNull_GLContextType, _8888, 0)
    #if SK_ANGLE
        GPU_TARGET(angle, GrContextFactory::kANGLE_GLContextType, _8888, 0)
    #endif
#endif
}

int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
    SetupCrashHandler();
    SkAutoGraphics ag;
    SkCommandLineFlags::Parse(argc, argv);

    const double overhead = estimate_timer_overhead();
    SkAutoTMalloc<double> samples(FLAGS_samples);

    // TODO: display add median, use it in --quiet mode

    if (FLAGS_verbose) {
        // No header.
    } else if (FLAGS_quiet) {
        SkDebugf("min\tbench\tconfig\n");
    } else {
        SkDebugf("loops\tmin\tmean\tmax\tstddev\tconfig\tbench\n");
    }

    for (const BenchRegistry* r = BenchRegistry::Head(); r != NULL; r = r->next()) {
        SkAutoTDelete<Benchmark> bench(r->factory()(NULL));
        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) {
            continue;
        }

        SkTDArray<Target*> targets;
        create_targets(bench.get(), &targets);

        bench->preDraw();
        for (int j = 0; j < targets.count(); j++) {
            SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;

            const int loops =
#if SK_SUPPORT_GPU
                Benchmark::kGPU_Backend == targets[j]->backend
                ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get())
                :
#endif
                 cpu_bench(       overhead, bench.get(), canvas, samples.get());

            Stats stats(samples.get(), FLAGS_samples);

            const char* config = targets[j]->config;
            if (FLAGS_verbose) {
                for (int i = 0; i < FLAGS_samples; i++) {
                    SkDebugf("%s  ", humanize(samples[i]).c_str());
                }
                SkDebugf("%s\n", bench->getName());
            } else if (FLAGS_quiet) {
                if (targets.count() == 1) {
                    config = ""; // Only print the config if we run the same bench on more than one.
                }
                SkDebugf("%s\t%s\t%s\n", humanize(stats.min).c_str(), bench->getName(), config);
            } else {
                const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
                SkDebugf("%d\t%s\t%s\t%s\t%.0f%%\t%s\t%s\n"
                        , loops
                        , humanize(stats.min).c_str()
                        , humanize(stats.mean).c_str()
                        , humanize(stats.max).c_str()
                        , stddev_percent
                        , config
                        , bench->getName()
                        );
            }
        }
        targets.deleteAll();
    }

    return 0;
}

#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
    return tool_main(argc, (char**) argv);
}
#endif
<commit_msg>Hey, maybe we should _call_ glFinish.<commit_after>/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <ctype.h>

#include "Benchmark.h"
#include "CrashHandler.h"
#include "Stats.h"
#include "Timer.h"

#include "SkCanvas.h"
#include "SkCommandLineFlags.h"
#include "SkForceLinking.h"
#include "SkGraphics.h"
#include "SkString.h"
#include "SkSurface.h"

#if SK_SUPPORT_GPU
    #include "GrContextFactory.h"
    GrContextFactory gGrFactory;
#endif

__SK_FORCE_IMAGE_DECODER_LINKING;

DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
DEFINE_double(overheadGoal, 0.0001,
              "Loop until timer overhead is at most this fraction of our measurments.");
DEFINE_string(match, "", "The usual filters on file names of benchmarks to measure.");
DEFINE_bool2(quiet, q, false, "Print only bench name and minimum sample.");
DEFINE_bool2(verbose, v, false, "Print all samples.");
DEFINE_string(config, "nonrendering 8888 gpu", "Configs to measure. Options: "
              "565 8888 gpu nonrendering debug nullgpu msaa4 msaa16 nvprmsaa4 nvprmsaa16 angle");
DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");

static SkString humanize(double ms) {
    if (ms > 1e+3) return SkStringPrintf("%.3gs",  ms/1e3);
    if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
    if (ms < 1)    return SkStringPrintf("%.3gµs", ms*1e3);
    return SkStringPrintf("%.3gms", ms);
}

static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) {
    WallTimer timer;
    timer.start();
    if (bench) {
        bench->draw(loops, canvas);
    }
    if (canvas) {
        canvas->flush();
    }
#if SK_SUPPORT_GPU
    if (gl) {
        SK_GL(*gl, Flush());
        gl->swapBuffers();
    }
#endif
    timer.end();
    return timer.fWall;
}

static double estimate_timer_overhead() {
    double overhead = 0;
    for (int i = 0; i < FLAGS_overheadLoops; i++) {
        overhead += time(1, NULL, NULL, NULL);
    }
    return overhead / FLAGS_overheadLoops;
}

static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
    // First figure out approximately how many loops of bench it takes to make overhead negligible.
    double bench_plus_overhead;
    do {
        bench_plus_overhead = time(1, bench, canvas, NULL);
    } while (bench_plus_overhead < overhead);  // Shouldn't normally happen.

    // Later we'll just start and stop the timer once but loop N times.
    // We'll pick N to make timer overhead negligible:
    //
    //          overhead
    //  -------------------------  < FLAGS_overheadGoal
    //  overhead + N * Bench Time
    //
    // where bench_plus_overhead ≈ overhead + Bench Time.
    //
    // Doing some math, we get:
    //
    //  (overhead / FLAGS_overheadGoal) - overhead
    //  ------------------------------------------  < N
    //       bench_plus_overhead - overhead)
    //
    // Luckily, this also works well in practice. :)
    const double numer = overhead / FLAGS_overheadGoal - overhead;
    const double denom = bench_plus_overhead - overhead;
    const int loops = (int)ceil(numer / denom);

    for (int i = 0; i < FLAGS_samples; i++) {
        samples[i] = time(loops, bench, canvas, NULL) / loops;
    }
    return loops;
}

#if SK_SUPPORT_GPU
static int gpu_bench(SkGLContextHelper* gl,
                     Benchmark* bench,
                     SkCanvas* canvas,
                     double* samples) {
    // Make sure we're done with whatever came before.
    SK_GL(*gl, Finish());

    // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
    int loops = 1;
    double elapsed = 0;
    do {
        loops *= 2;
        // If the GPU lets frames lag at all, we need to make sure we're timing
        // _this_ round, not still timing last round.  We force this by looping
        // more times than any reasonable GPU will allow frames to lag.
        for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
            elapsed = time(loops, bench, canvas, gl);
        }
    } while (elapsed < FLAGS_gpuMs);

    // We've overshot at least a little.  Scale back linearly.
    loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);

    // Might as well make sure we're not still timing our calibration.
    SK_GL(*gl, Finish());

    // Pretty much the same deal as the calibration: do some warmup to make
    // sure we're timing steady-state pipelined frames.
    for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
        time(loops, bench, canvas, gl);
    }

    // Now, actually do the timing!
    for (int i = 0; i < FLAGS_samples; i++) {
        samples[i] = time(loops, bench, canvas, gl) / loops;
    }
    return loops;
}
#endif

static SkString to_lower(const char* str) {
    SkString lower(str);
    for (size_t i = 0; i < lower.size(); i++) {
        lower[i] = tolower(lower[i]);
    }
    return lower;
}

struct Target {
    const char* config;
    Benchmark::Backend backend;
    SkAutoTDelete<SkSurface> surface;
#if SK_SUPPORT_GPU
    SkGLContextHelper* gl;
#endif
};

// If bench is enabled for backend/config, returns a Target* for them, otherwise NULL.
static Target* is_enabled(Benchmark* bench, Benchmark::Backend backend, const char* config) {
    if (!bench->isSuitableFor(backend)) {
        return NULL;
    }

    for (int i = 0; i < FLAGS_config.count(); i++) {
        if (to_lower(FLAGS_config[i]).equals(config)) {
            Target* target = new Target;
            target->config  = config;
            target->backend = backend;
            return target;
        }
    }
    return NULL;
}

// Append all targets that are suitable for bench.
static void create_targets(Benchmark* bench, SkTDArray<Target*>* targets) {
    const int w = bench->getSize().fX,
              h = bench->getSize().fY;
    const SkImageInfo _8888 = { w, h, kN32_SkColorType,     kPremul_SkAlphaType },
                       _565 = { w, h, kRGB_565_SkColorType, kOpaque_SkAlphaType };

    #define CPU_TARGET(config, backend, code)                              \
        if (Target* t = is_enabled(bench, Benchmark::backend, #config)) {  \
            t->surface.reset(code);                                        \
            targets->push(t);                                              \
        }
    CPU_TARGET(nonrendering, kNonRendering_Backend, NULL)
    CPU_TARGET(8888, kRaster_Backend, SkSurface::NewRaster(_8888))
    CPU_TARGET(565,  kRaster_Backend, SkSurface::NewRaster(_565))

#if SK_SUPPORT_GPU
    #define GPU_TARGET(config, ctxType, info, samples)                                            \
        if (Target* t = is_enabled(bench, Benchmark::kGPU_Backend, #config)) {                    \
            t->surface.reset(SkSurface::NewRenderTarget(gGrFactory.get(ctxType), info, samples)); \
            t->gl = gGrFactory.getGLContext(ctxType);                                             \
            targets->push(t);                                                                     \
        }
    GPU_TARGET(gpu,      GrContextFactory::kNative_GLContextType, _8888, 0)
    GPU_TARGET(msaa4,    GrContextFactory::kNative_GLContextType, _8888, 4)
    GPU_TARGET(msaa16,   GrContextFactory::kNative_GLContextType, _8888, 16)
    GPU_TARGET(nvprmsaa4,  GrContextFactory::kNVPR_GLContextType, _8888, 4)
    GPU_TARGET(nvprmsaa16, GrContextFactory::kNVPR_GLContextType, _8888, 16)
    GPU_TARGET(debug,     GrContextFactory::kDebug_GLContextType, _8888, 0)
    GPU_TARGET(nullgpu,    GrContextFactory::kNull_GLContextType, _8888, 0)
    #if SK_ANGLE
        GPU_TARGET(angle, GrContextFactory::kANGLE_GLContextType, _8888, 0)
    #endif
#endif
}

int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
    SetupCrashHandler();
    SkAutoGraphics ag;
    SkCommandLineFlags::Parse(argc, argv);

    const double overhead = estimate_timer_overhead();
    SkAutoTMalloc<double> samples(FLAGS_samples);

    // TODO: display add median, use it in --quiet mode

    if (FLAGS_verbose) {
        // No header.
    } else if (FLAGS_quiet) {
        SkDebugf("min\tbench\tconfig\n");
    } else {
        SkDebugf("loops\tmin\tmean\tmax\tstddev\tconfig\tbench\n");
    }

    for (const BenchRegistry* r = BenchRegistry::Head(); r != NULL; r = r->next()) {
        SkAutoTDelete<Benchmark> bench(r->factory()(NULL));
        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) {
            continue;
        }

        SkTDArray<Target*> targets;
        create_targets(bench.get(), &targets);

        bench->preDraw();
        for (int j = 0; j < targets.count(); j++) {
            SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;

            const int loops =
#if SK_SUPPORT_GPU
                Benchmark::kGPU_Backend == targets[j]->backend
                ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get())
                :
#endif
                 cpu_bench(       overhead, bench.get(), canvas, samples.get());

            Stats stats(samples.get(), FLAGS_samples);

            const char* config = targets[j]->config;
            if (FLAGS_verbose) {
                for (int i = 0; i < FLAGS_samples; i++) {
                    SkDebugf("%s  ", humanize(samples[i]).c_str());
                }
                SkDebugf("%s\n", bench->getName());
            } else if (FLAGS_quiet) {
                if (targets.count() == 1) {
                    config = ""; // Only print the config if we run the same bench on more than one.
                }
                SkDebugf("%s\t%s\t%s\n", humanize(stats.min).c_str(), bench->getName(), config);
            } else {
                const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
                SkDebugf("%d\t%s\t%s\t%s\t%.0f%%\t%s\t%s\n"
                        , loops
                        , humanize(stats.min).c_str()
                        , humanize(stats.mean).c_str()
                        , humanize(stats.max).c_str()
                        , stddev_percent
                        , config
                        , bench->getName()
                        );
            }
        }
        targets.deleteAll();
    }

    return 0;
}

#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
    return tool_main(argc, (char**) argv);
}
#endif
<|endoftext|>


<commit_before>/*$Log$
 *Revision 1.23  2003/10/09 09:42:42  strk
 *Tried to "formalize" constant nature of the first argument given to constructor by PrecisionModel and SRID specification. Added CVS Log on top.
 **/
#include "../headers/geom.h"
#include "../headers/util.h"
#include <typeinfo>

namespace geos {

GeometryFactory::GeometryFactory() {
	precisionModel=new PrecisionModel();
	SRID=0;
}

/**
* Create a geometry factory using given precision model and srid.
* Will make a copy of given PrecisionModel object, so callers
* can safely delete it afterwards.
* We should use the 'const' modifier, but that made my compiler
* crazy !! --strk;
*/
GeometryFactory::GeometryFactory(PrecisionModel* pm, int newSRID){
    precisionModel=new PrecisionModel(*pm);
    SRID=newSRID;
}
GeometryFactory::~GeometryFactory(){
	delete precisionModel;
}

Point* GeometryFactory::createPointFromInternalCoord(Coordinate* coord, Geometry *exemplar) {
	exemplar->getPrecisionModel()->makePrecise(coord);
	return new Point(*coord,exemplar->getPrecisionModel(),exemplar->getSRID());
}

Geometry* GeometryFactory::toGeometry(Envelope* envelope,PrecisionModel* precisionModel,int SRID) {
	if (envelope->isNull()) {
		return new Point(*(new Coordinate()),precisionModel,SRID);
	}
	if (envelope->getMinX()==envelope->getMaxX() && envelope->getMinY()==envelope->getMaxY()) {
		return new Point(*(new Coordinate(envelope->getMinX(),envelope->getMinY())),precisionModel,SRID);
	}
	CoordinateList *cl=CoordinateListFactory::internalFactory->createCoordinateList();
	cl->add(*(new Coordinate(envelope->getMinX(), envelope->getMinY())));
	cl->add(*(new Coordinate(envelope->getMaxX(), envelope->getMinY())));
	cl->add(*(new Coordinate(envelope->getMaxX(), envelope->getMaxY())));
	cl->add(*(new Coordinate(envelope->getMinX(), envelope->getMaxY())));
	cl->add(*(new Coordinate(envelope->getMinX(), envelope->getMinY())));
	return new Polygon(new LinearRing(cl,precisionModel,SRID),precisionModel,SRID);
}

PrecisionModel* GeometryFactory::getPrecisionModel(){
	return precisionModel;
}

Point* GeometryFactory::createPoint(Coordinate& coordinate){
	return new Point(coordinate,precisionModel,SRID);
}

MultiLineString* GeometryFactory::createMultiLineString(vector<Geometry *> *lineStrings){
	return new MultiLineString(lineStrings,precisionModel,SRID);
}

GeometryCollection* GeometryFactory::createGeometryCollection(vector<Geometry *> *geometries){
	return new GeometryCollection(geometries,precisionModel,SRID);
}

MultiPolygon* GeometryFactory::createMultiPolygon(vector<Geometry *> *polygons){
	return new MultiPolygon(polygons,precisionModel,SRID);
}

LinearRing* GeometryFactory::createLinearRing(CoordinateList* coordinates) {
	if (coordinates->getSize()>0 &&
		!coordinates->getAt(0).equals2D(coordinates->getAt(coordinates->getSize() - 1))) {
			delete precisionModel;
			throw new IllegalArgumentException("LinearRing not closed");
	}
	return new LinearRing(coordinates, precisionModel, SRID);
}

MultiPoint* GeometryFactory::createMultiPoint(vector<Geometry *> *point) {
	return new MultiPoint(point,precisionModel,SRID);
}

MultiPoint* GeometryFactory::createMultiPoint(CoordinateList* coordinates) {
	vector<Geometry *> *pts=new vector<Geometry *>();
	for (int i=0; i<coordinates->getSize(); i++) {
		Point *pt=createPoint(coordinates->getAt(i));
		pts->push_back(pt);
	}
	delete coordinates;
	return createMultiPoint(pts);
}

Polygon* GeometryFactory::createPolygon(LinearRing *shell, vector<Geometry *> *holes) {
	return new Polygon(shell, holes, precisionModel, SRID);
}

LineString* GeometryFactory::createLineString(CoordinateList* coordinates) {
	return new LineString(coordinates, precisionModel, SRID);
}

Geometry* GeometryFactory::buildGeometry(vector<Geometry *> *geoms) {
	string geomClass("NULL");
	bool isHeterogeneous=false;
	bool isCollection=geoms->size()>1;
	unsigned int i;

	for (i=0; i<geoms->size(); i++) {
		string partClass(typeid(*(*geoms)[i]).name());
		if (geomClass=="NULL") {
			geomClass=partClass;
		}
		if (partClass!=geomClass) {
			isHeterogeneous = true;
		}
	}
    // for the empty geometry, return an empty GeometryCollection
	if (geomClass=="NULL") {
		return createGeometryCollection(NULL);
	}
	if (isHeterogeneous) {
		return createGeometryCollection(new vector<Geometry*>(*geoms));
	}
	Geometry *geom0=(*geoms)[0];
	if (isCollection) {
		if (typeid(*geom0)==typeid(Polygon)) {
			return createMultiPolygon(new vector<Geometry*>(*geoms));
		} else if (typeid(*geom0)==typeid(LineString)) {
			return createMultiLineString(new vector<Geometry*>(*geoms));
		} else if (typeid(*geom0)==typeid(Point)) {
			return createMultiPoint(new vector<Geometry*>(*geoms));
		}
		Assert::shouldNeverReachHere();
	}
	if (typeid(*geom0)==typeid(Polygon))
		return (Polygon*) geom0;
	// LineString also handles LinearRings
	else if (typeid(*geom0)==typeid(LineString))
		return (LineString*) geom0;
	else if (typeid(*geom0)==typeid(Point))
		return (Point*) geom0;
	else if (typeid(*geom0)==typeid(MultiPoint))
		return (MultiPoint*) geom0;
	else if (typeid(*geom0)==typeid(MultiLineString))
		return (MultiLineString*) geom0;
	else if (typeid(*geom0)==typeid(MultiPolygon))
		return (MultiPolygon*) geom0;
	else if (typeid(*geom0)==typeid(GeometryCollection))
		return (GeometryCollection*) geom0;
	else
		return geom0;
}

}

<commit_msg>just a style change in top Log comment.<commit_after>/*
* $Log$
* Revision 1.24  2003/10/09 10:14:06  strk
* just a style change in top Log comment.
*
*/
#include "../headers/geom.h"
#include "../headers/util.h"
#include <typeinfo>

namespace geos {

GeometryFactory::GeometryFactory() {
	precisionModel=new PrecisionModel();
	SRID=0;
}

/**
* Create a geometry factory using given precision model and srid.
* Will make a copy of given PrecisionModel object, so callers
* can safely delete it afterwards.
* We should use the 'const' modifier, but that made my compiler
* crazy !! --strk;
*/
GeometryFactory::GeometryFactory(PrecisionModel* pm, int newSRID){
    precisionModel=new PrecisionModel(*pm);
    SRID=newSRID;
}
GeometryFactory::~GeometryFactory(){
	delete precisionModel;
}

Point* GeometryFactory::createPointFromInternalCoord(Coordinate* coord, Geometry *exemplar) {
	exemplar->getPrecisionModel()->makePrecise(coord);
	return new Point(*coord,exemplar->getPrecisionModel(),exemplar->getSRID());
}

Geometry* GeometryFactory::toGeometry(Envelope* envelope,PrecisionModel* precisionModel,int SRID) {
	if (envelope->isNull()) {
		return new Point(*(new Coordinate()),precisionModel,SRID);
	}
	if (envelope->getMinX()==envelope->getMaxX() && envelope->getMinY()==envelope->getMaxY()) {
		return new Point(*(new Coordinate(envelope->getMinX(),envelope->getMinY())),precisionModel,SRID);
	}
	CoordinateList *cl=CoordinateListFactory::internalFactory->createCoordinateList();
	cl->add(*(new Coordinate(envelope->getMinX(), envelope->getMinY())));
	cl->add(*(new Coordinate(envelope->getMaxX(), envelope->getMinY())));
	cl->add(*(new Coordinate(envelope->getMaxX(), envelope->getMaxY())));
	cl->add(*(new Coordinate(envelope->getMinX(), envelope->getMaxY())));
	cl->add(*(new Coordinate(envelope->getMinX(), envelope->getMinY())));
	return new Polygon(new LinearRing(cl,precisionModel,SRID),precisionModel,SRID);
}

PrecisionModel* GeometryFactory::getPrecisionModel(){
	return precisionModel;
}

Point* GeometryFactory::createPoint(Coordinate& coordinate){
	return new Point(coordinate,precisionModel,SRID);
}

MultiLineString* GeometryFactory::createMultiLineString(vector<Geometry *> *lineStrings){
	return new MultiLineString(lineStrings,precisionModel,SRID);
}

GeometryCollection* GeometryFactory::createGeometryCollection(vector<Geometry *> *geometries){
	return new GeometryCollection(geometries,precisionModel,SRID);
}

MultiPolygon* GeometryFactory::createMultiPolygon(vector<Geometry *> *polygons){
	return new MultiPolygon(polygons,precisionModel,SRID);
}

LinearRing* GeometryFactory::createLinearRing(CoordinateList* coordinates) {
	if (coordinates->getSize()>0 &&
		!coordinates->getAt(0).equals2D(coordinates->getAt(coordinates->getSize() - 1))) {
			delete precisionModel;
			throw new IllegalArgumentException("LinearRing not closed");
	}
	return new LinearRing(coordinates, precisionModel, SRID);
}

MultiPoint* GeometryFactory::createMultiPoint(vector<Geometry *> *point) {
	return new MultiPoint(point,precisionModel,SRID);
}

MultiPoint* GeometryFactory::createMultiPoint(CoordinateList* coordinates) {
	vector<Geometry *> *pts=new vector<Geometry *>();
	for (int i=0; i<coordinates->getSize(); i++) {
		Point *pt=createPoint(coordinates->getAt(i));
		pts->push_back(pt);
	}
	delete coordinates;
	return createMultiPoint(pts);
}

Polygon* GeometryFactory::createPolygon(LinearRing *shell, vector<Geometry *> *holes) {
	return new Polygon(shell, holes, precisionModel, SRID);
}

LineString* GeometryFactory::createLineString(CoordinateList* coordinates) {
	return new LineString(coordinates, precisionModel, SRID);
}

Geometry* GeometryFactory::buildGeometry(vector<Geometry *> *geoms) {
	string geomClass("NULL");
	bool isHeterogeneous=false;
	bool isCollection=geoms->size()>1;
	unsigned int i;

	for (i=0; i<geoms->size(); i++) {
		string partClass(typeid(*(*geoms)[i]).name());
		if (geomClass=="NULL") {
			geomClass=partClass;
		}
		if (partClass!=geomClass) {
			isHeterogeneous = true;
		}
	}
    // for the empty geometry, return an empty GeometryCollection
	if (geomClass=="NULL") {
		return createGeometryCollection(NULL);
	}
	if (isHeterogeneous) {
		return createGeometryCollection(new vector<Geometry*>(*geoms));
	}
	Geometry *geom0=(*geoms)[0];
	if (isCollection) {
		if (typeid(*geom0)==typeid(Polygon)) {
			return createMultiPolygon(new vector<Geometry*>(*geoms));
		} else if (typeid(*geom0)==typeid(LineString)) {
			return createMultiLineString(new vector<Geometry*>(*geoms));
		} else if (typeid(*geom0)==typeid(Point)) {
			return createMultiPoint(new vector<Geometry*>(*geoms));
		}
		Assert::shouldNeverReachHere();
	}
	if (typeid(*geom0)==typeid(Polygon))
		return (Polygon*) geom0;
	// LineString also handles LinearRings
	else if (typeid(*geom0)==typeid(LineString))
		return (LineString*) geom0;
	else if (typeid(*geom0)==typeid(Point))
		return (Point*) geom0;
	else if (typeid(*geom0)==typeid(MultiPoint))
		return (MultiPoint*) geom0;
	else if (typeid(*geom0)==typeid(MultiLineString))
		return (MultiLineString*) geom0;
	else if (typeid(*geom0)==typeid(MultiPolygon))
		return (MultiPolygon*) geom0;
	else if (typeid(*geom0)==typeid(GeometryCollection))
		return (GeometryCollection*) geom0;
	else
		return geom0;
}

}

<|endoftext|>


<commit_before>/*******************************************************************************
 * tests/net/group_test.cpp
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#include <c7a/net/collective_communication.hpp>
#include <c7a/net/dispatcher.hpp>
#include <c7a/net/flow_control_channel.hpp>
#include <c7a/net/group.hpp>
#include <c7a/net/manager.hpp>
#include <gtest/gtest.h>

#include <random>
#include <string>
#include <thread>
#include <vector>

using namespace c7a::net;

static void ThreadInitializeAsyncRead(Group* net) {
    // send a message to all other clients except ourselves.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->connection(i).GetSocket().send(&i, sizeof(size_t));
    }

    size_t received = 0;
    Dispatcher dispatcher;

    Dispatcher::AsyncReadCallback callback =
        [net, &received](Connection& /* s */, const Buffer& buffer) {
            ASSERT_EQ(*(reinterpret_cast<const size_t*>(buffer.data())),
                      net->MyRank());
            received++;
        };

    // add async reads to net dispatcher
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        dispatcher.AsyncRead(net->connection(i), sizeof(size_t), callback);
    }

    while (received < net->Size() - 1) {
        dispatcher.Dispatch();
    }
}

static void ThreadInitializeSendCyclic(Group* net) {

    size_t id = net->MyRank();

    if (id != 0) {
        size_t res;
        net->ReceiveFrom<size_t>(id - 1, &res);
        ASSERT_EQ(id - 1, res);
    }

    if (id != net->Size() - 1) {
        net->SendTo(id + 1, id);
    }
}

static void ThreadInitializeBroadcastIntegral(Group* net) {

    static const bool debug = false;

    //Broadcast our ID to everyone
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->SendTo(i, net->MyRank());
    }

    //Receive the id from everyone. Make sure that the id is correct.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;

        size_t val;
        ClientId id;

        net->ReceiveFromAny<size_t>(&id, &val);

        LOG << "Received " << val << " from " << id;

        ASSERT_EQ((int)id, (int)val);
    }
}

static void ThreadInitializeSendReceive(Group* net) {
    static const bool debug = false;

    // send a message to all other clients except ourselves.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->SendStringTo(i, "Hello " + std::to_string(net->MyRank())
                          + " -> " + std::to_string(i));
    }
    // receive the n-1 messages from clients in order
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;

        std::string msg;
        net->ReceiveStringFrom(i, &msg);
        sLOG << "Received from client" << i << "msg" << msg;

        ASSERT_EQ(msg, "Hello " + std::to_string(i)
                  + " -> " + std::to_string(net->MyRank()));
    }

    // *****************************************************************

    // send another message to all other clients except ourselves. Now with connection access.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->connection(i).SendString("Hello " + std::to_string(net->MyRank())
                                      + " -> " + std::to_string(i));
    }
    // receive the n-1 messages from clients in any order
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;

        ClientId from;
        std::string msg;
        net->ReceiveStringFromAny(&from, &msg);
        sLOG << "Received from client" << i << "msg" << msg;

        ASSERT_EQ(msg, "Hello " + std::to_string(from)
                  + " -> " + std::to_string(net->MyRank()));
    }
}

static void ThreadInitializeSendReceiveALot(Group* net) {
    for (int i = 0; i < 100; i++) {
        ThreadInitializeSendReceive(net);
    }
}

static void ThreadInitializeSendReceiveAsyncALot(Group* net) {
    for (int i = 0; i < 100; i++) {
        ThreadInitializeAsyncRead(net);
    }
}

static void RealGroupConstructAndCall(
    std::function<void(Group*)> thread_function) {
    // randomize base port number for test
    std::random_device random_device;
    std::default_random_engine generator(random_device());
    std::uniform_int_distribution<int> distribution(10000, 30000);
    const size_t port_base = distribution(generator);

    std::vector<Endpoint> endpoints = {
        Endpoint("127.0.0.1:" + std::to_string(port_base + 0)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 1)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 2)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 3)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 4)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 5))
    };

    sLOG1 << "Group test uses ports" << port_base << "-" << port_base + 5;

    const size_t count = endpoints.size();

    std::vector<std::thread> threads(count);

    // lambda to construct Group and call user thread function.

    std::vector<Manager> groups(count);

    for (size_t i = 0; i < count; i++) {
        threads[i] = std::thread(
            [i, &endpoints, thread_function, &groups]() {
                // construct Group i with endpoints
                groups[i].Initialize(i, endpoints);
                // run thread function
                thread_function(&groups[i].GetFlowGroup());
            });
    }

    for (size_t i = 0; i < count; i++) {
        threads[i].join();
    }
    for (size_t i = 0; i < count; i++) {
        groups[i].Close();
    }
}

TEST(Group, RealInitializeAndClose) {
    // Construct a real Group of 6 workers which do nothing but terminate.
    RealGroupConstructAndCall([](Group*) { });
}

TEST(Group, RealInitializeSendReceive) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all neighbors.
    RealGroupConstructAndCall(ThreadInitializeSendReceive);
}

TEST(Group, RealInitializeSendReceiveALot) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all neighbors.
    RealGroupConstructAndCall(ThreadInitializeSendReceive);
}

TEST(Group, RealInitializeSendReceiveAsync) { //TODO(ej) test hangs from time to time
    // Construct a real Group of 6 workers which execute the thread function
    // which sends and receives asynchronous messages between all workers.
    RealGroupConstructAndCall(ThreadInitializeAsyncRead);
}

TEST(Group, RealInitializeSendReceiveAsyncALot) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all neighbors.
    RealGroupConstructAndCall(ThreadInitializeSendReceive);
}

TEST(Group, RealInitializeBroadcast) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all workers.
    RealGroupConstructAndCall(ThreadInitializeBroadcastIntegral);
}
TEST(Group, RealSendCyclic) {
    RealGroupConstructAndCall(ThreadInitializeSendCyclic);
}

TEST(Group, InitializeAndClose) {
    // Construct a Group of 6 workers which do nothing but terminate.
    Group::ExecuteLocalMock(6, [](Group*) { });
}

TEST(Group, InitializeSendReceive) {
    // Construct a Group of 6 workers which execute the thread function
    // which sends and receives asynchronous messages between all workers.
    Group::ExecuteLocalMock(6, ThreadInitializeSendReceive);
}

TEST(Group, InitializeBroadcast) {
    // Construct a Group of 6 workers which execute the thread function
    // above which sends and receives a message from all workers.
    Group::ExecuteLocalMock(6, ThreadInitializeBroadcastIntegral);
}

TEST(Group, SendCyclic) {
    Group::ExecuteLocalMock(6, ThreadInitializeSendCyclic);
}

TEST(Group, TestPrefixSum) {
    for (size_t p = 1; p <= 8; ++p) {
        // Construct Group of p workers which perform a PrefixSum collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value = 1;
                PrefixSum(*net, local_value);
                ASSERT_EQ(local_value, net->MyRank() + 1);
            });
    }
}

TEST(Group, TestAllReduce) {
    for (size_t p = 0; p <= 8; ++p) {
        // Construct Group of p workers which perform an AllReduce collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value = net->MyRank();
                AllReduce(*net, local_value);
                ASSERT_EQ(local_value, net->Size() * (net->Size() - 1) / 2);
            });
    }
}

TEST(Group, TestBroadcast) {
    for (size_t p = 0; p <= 8; ++p) {
        // Construct Group of p workers which perform an Broadcast collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value;
                if (net->MyRank() == 0) local_value = 42;
                Broadcast(*net, local_value);
                ASSERT_EQ(local_value, 42u);
            });
    }
}

TEST(Group, TestReduceToRoot) {
    for (size_t p = 0; p <= 8; ++p) {
        // Construct Group of p workers which perform an Broadcast collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value = net->MyRank();
                ReduceToRoot(*net, local_value);
                if (net->MyRank() == 0)
                    ASSERT_EQ(local_value, net->Size() * (net->Size() - 1) / 2);
            });
    }
}

TEST(Group, TestBarrier) {
    static const bool debug = false;
    std::mutex sync_mtx;            // Synchronisation mutex for the barrier
    std::mutex local_mtx;           // Mutex for writing to the results array
    std::condition_variable cv;     // Condition variable for the barrier

    for (int p = 0; p <= 8; ++p) {
        int workers = p;
        int workers_copy = workers; // Will be decremented by the barrier function

        std::vector<char> result(2 * workers);
        int k = 0;                  // The counter for the result array
        sLOG << "I'm in test" << workers;

        Group::ExecuteLocalMock(
            workers, [&](Group* net) {
                local_mtx.lock();
                result[k++] = 'B'; // B stands for 'Before barrier'
                local_mtx.unlock();

                sLOG << "Before Barrier, worker" << net->MyRank();

                ThreadBarrier(sync_mtx, cv, workers_copy);

                local_mtx.lock();
                result[k++] = 'A'; // A stands for 'After barrier'
                local_mtx.unlock();

                sLOG << "After Barrier, worker" << net->MyRank();
            });
        for (int i = 0; i < workers; ++i) {
            sLOG << "Checking position" << i;
            ASSERT_EQ(result[i], 'B');
        }
        for (int i = workers; i < 2 * workers; ++i) {
            sLOG << "Checking position" << i;
            ASSERT_EQ(result[i], 'A');
        }
    }
}

/******************************************************************************/
<commit_msg>Removing two tests that generate "unused" warnings.<commit_after>/*******************************************************************************
 * tests/net/group_test.cpp
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#include <c7a/net/collective_communication.hpp>
#include <c7a/net/dispatcher.hpp>
#include <c7a/net/flow_control_channel.hpp>
#include <c7a/net/group.hpp>
#include <c7a/net/manager.hpp>
#include <gtest/gtest.h>

#include <random>
#include <string>
#include <thread>
#include <vector>

using namespace c7a::net;

static void ThreadInitializeAsyncRead(Group* net) {
    // send a message to all other clients except ourselves.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->connection(i).GetSocket().send(&i, sizeof(size_t));
    }

    size_t received = 0;
    Dispatcher dispatcher;

    Dispatcher::AsyncReadCallback callback =
        [net, &received](Connection& /* s */, const Buffer& buffer) {
            ASSERT_EQ(*(reinterpret_cast<const size_t*>(buffer.data())),
                      net->MyRank());
            received++;
        };

    // add async reads to net dispatcher
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        dispatcher.AsyncRead(net->connection(i), sizeof(size_t), callback);
    }

    while (received < net->Size() - 1) {
        dispatcher.Dispatch();
    }
}

static void ThreadInitializeSendCyclic(Group* net) {

    size_t id = net->MyRank();

    if (id != 0) {
        size_t res;
        net->ReceiveFrom<size_t>(id - 1, &res);
        ASSERT_EQ(id - 1, res);
    }

    if (id != net->Size() - 1) {
        net->SendTo(id + 1, id);
    }
}

static void ThreadInitializeBroadcastIntegral(Group* net) {

    static const bool debug = false;

    //Broadcast our ID to everyone
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->SendTo(i, net->MyRank());
    }

    //Receive the id from everyone. Make sure that the id is correct.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;

        size_t val;
        ClientId id;

        net->ReceiveFromAny<size_t>(&id, &val);

        LOG << "Received " << val << " from " << id;

        ASSERT_EQ((int)id, (int)val);
    }
}

static void ThreadInitializeSendReceive(Group* net) {
    static const bool debug = false;

    // send a message to all other clients except ourselves.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->SendStringTo(i, "Hello " + std::to_string(net->MyRank())
                          + " -> " + std::to_string(i));
    }
    // receive the n-1 messages from clients in order
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;

        std::string msg;
        net->ReceiveStringFrom(i, &msg);
        sLOG << "Received from client" << i << "msg" << msg;

        ASSERT_EQ(msg, "Hello " + std::to_string(i)
                  + " -> " + std::to_string(net->MyRank()));
    }

    // *****************************************************************

    // send another message to all other clients except ourselves. Now with connection access.
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;
        net->connection(i).SendString("Hello " + std::to_string(net->MyRank())
                                      + " -> " + std::to_string(i));
    }
    // receive the n-1 messages from clients in any order
    for (size_t i = 0; i != net->Size(); ++i)
    {
        if (i == net->MyRank()) continue;

        ClientId from;
        std::string msg;
        net->ReceiveStringFromAny(&from, &msg);
        sLOG << "Received from client" << i << "msg" << msg;

        ASSERT_EQ(msg, "Hello " + std::to_string(from)
                  + " -> " + std::to_string(net->MyRank()));
    }
}

static void RealGroupConstructAndCall(
    std::function<void(Group*)> thread_function) {
    // randomize base port number for test
    std::random_device random_device;
    std::default_random_engine generator(random_device());
    std::uniform_int_distribution<int> distribution(10000, 30000);
    const size_t port_base = distribution(generator);

    std::vector<Endpoint> endpoints = {
        Endpoint("127.0.0.1:" + std::to_string(port_base + 0)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 1)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 2)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 3)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 4)),
        Endpoint("127.0.0.1:" + std::to_string(port_base + 5))
    };

    sLOG1 << "Group test uses ports" << port_base << "-" << port_base + 5;

    const size_t count = endpoints.size();

    std::vector<std::thread> threads(count);

    // lambda to construct Group and call user thread function.

    std::vector<Manager> groups(count);

    for (size_t i = 0; i < count; i++) {
        threads[i] = std::thread(
            [i, &endpoints, thread_function, &groups]() {
                // construct Group i with endpoints
                groups[i].Initialize(i, endpoints);
                // run thread function
                thread_function(&groups[i].GetFlowGroup());
            });
    }

    for (size_t i = 0; i < count; i++) {
        threads[i].join();
    }
    for (size_t i = 0; i < count; i++) {
        groups[i].Close();
    }
}

TEST(Group, RealInitializeAndClose) {
    // Construct a real Group of 6 workers which do nothing but terminate.
    RealGroupConstructAndCall([](Group*) { });
}

TEST(Group, RealInitializeSendReceive) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all neighbors.
    RealGroupConstructAndCall(ThreadInitializeSendReceive);
}

TEST(Group, RealInitializeSendReceiveALot) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all neighbors.
    RealGroupConstructAndCall(ThreadInitializeSendReceive);
}

TEST(Group, RealInitializeSendReceiveAsync) { //TODO(ej) test hangs from time to time
    // Construct a real Group of 6 workers which execute the thread function
    // which sends and receives asynchronous messages between all workers.
    RealGroupConstructAndCall(ThreadInitializeAsyncRead);
}

TEST(Group, RealInitializeSendReceiveAsyncALot) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all neighbors.
    RealGroupConstructAndCall(ThreadInitializeSendReceive);
}

TEST(Group, RealInitializeBroadcast) {
    // Construct a real Group of 6 workers which execute the thread function
    // above which sends and receives a message from all workers.
    RealGroupConstructAndCall(ThreadInitializeBroadcastIntegral);
}
TEST(Group, RealSendCyclic) {
    RealGroupConstructAndCall(ThreadInitializeSendCyclic);
}

TEST(Group, InitializeAndClose) {
    // Construct a Group of 6 workers which do nothing but terminate.
    Group::ExecuteLocalMock(6, [](Group*) { });
}

TEST(Group, InitializeSendReceive) {
    // Construct a Group of 6 workers which execute the thread function
    // which sends and receives asynchronous messages between all workers.
    Group::ExecuteLocalMock(6, ThreadInitializeSendReceive);
}

TEST(Group, InitializeBroadcast) {
    // Construct a Group of 6 workers which execute the thread function
    // above which sends and receives a message from all workers.
    Group::ExecuteLocalMock(6, ThreadInitializeBroadcastIntegral);
}

TEST(Group, SendCyclic) {
    Group::ExecuteLocalMock(6, ThreadInitializeSendCyclic);
}

TEST(Group, TestPrefixSum) {
    for (size_t p = 1; p <= 8; ++p) {
        // Construct Group of p workers which perform a PrefixSum collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value = 1;
                PrefixSum(*net, local_value);
                ASSERT_EQ(local_value, net->MyRank() + 1);
            });
    }
}

TEST(Group, TestAllReduce) {
    for (size_t p = 0; p <= 8; ++p) {
        // Construct Group of p workers which perform an AllReduce collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value = net->MyRank();
                AllReduce(*net, local_value);
                ASSERT_EQ(local_value, net->Size() * (net->Size() - 1) / 2);
            });
    }
}

TEST(Group, TestBroadcast) {
    for (size_t p = 0; p <= 8; ++p) {
        // Construct Group of p workers which perform an Broadcast collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value;
                if (net->MyRank() == 0) local_value = 42;
                Broadcast(*net, local_value);
                ASSERT_EQ(local_value, 42u);
            });
    }
}

TEST(Group, TestReduceToRoot) {
    for (size_t p = 0; p <= 8; ++p) {
        // Construct Group of p workers which perform an Broadcast collective
        Group::ExecuteLocalMock(
            p, [](Group* net) {
                size_t local_value = net->MyRank();
                ReduceToRoot(*net, local_value);
                if (net->MyRank() == 0)
                    ASSERT_EQ(local_value, net->Size() * (net->Size() - 1) / 2);
            });
    }
}

TEST(Group, TestBarrier) {
    static const bool debug = false;
    std::mutex sync_mtx;            // Synchronisation mutex for the barrier
    std::mutex local_mtx;           // Mutex for writing to the results array
    std::condition_variable cv;     // Condition variable for the barrier

    for (int p = 0; p <= 8; ++p) {
        int workers = p;
        int workers_copy = workers; // Will be decremented by the barrier function

        std::vector<char> result(2 * workers);
        int k = 0;                  // The counter for the result array
        sLOG << "I'm in test" << workers;

        Group::ExecuteLocalMock(
            workers, [&](Group* net) {
                local_mtx.lock();
                result[k++] = 'B'; // B stands for 'Before barrier'
                local_mtx.unlock();

                sLOG << "Before Barrier, worker" << net->MyRank();

                ThreadBarrier(sync_mtx, cv, workers_copy);

                local_mtx.lock();
                result[k++] = 'A'; // A stands for 'After barrier'
                local_mtx.unlock();

                sLOG << "After Barrier, worker" << net->MyRank();
            });
        for (int i = 0; i < workers; ++i) {
            sLOG << "Checking position" << i;
            ASSERT_EQ(result[i], 'B');
        }
        for (int i = workers; i < 2 * workers; ++i) {
            sLOG << "Checking position" << i;
            ASSERT_EQ(result[i], 'A');
        }
    }
}

/******************************************************************************/
<|endoftext|>