<commit_before>#include "model.h"
#include "recognition.h"
#include <memory>
#include <algorithm>
#include <vector>

using std::min;
using std::max;
using std::make_shared;
using std::dynamic_pointer_cast;

int FIGURE_SELECT_GAP = -1;
int CLOSED_FIGURE_GAP = -1;
const int TRACK_FIT_GAP = 10;
const int DELETION_MOVE_MINIMAL_LENGTH = 5;
const int DELETION_MOVE_MAX_ANGLE = 30;
const int DELETION_MOVE_MIN_COUNT = 3;
const double SQUARING_MIN_RATIO = 0.8;
const double MIN_FIT_POINTS_AMOUNT = 0.75;

void setRecognitionPreset(RecognitionPreset preset) {
    switch (preset) {
    case RecognitionPreset::Mouse:
        FIGURE_SELECT_GAP = 10;
        CLOSED_FIGURE_GAP = 10;
        break;
    case RecognitionPreset::Touch:
        FIGURE_SELECT_GAP = 30;
        CLOSED_FIGURE_GAP = 50;
        break;
    };
}

int figureSelectGap() {
    return FIGURE_SELECT_GAP;
}

BoundingBox getBoundingBox(const Track &track) {
    BoundingBox res;
    res.leftUp = res.rightDown = track[0];
    for (Point p : track.points) {
        res.leftUp.x = min(res.leftUp.x, p.x);
        res.leftUp.y = min(res.leftUp.y, p.y);
        res.rightDown.x = max(res.rightDown.x, p.x);
        res.rightDown.y = max(res.rightDown.y, p.y);
    }
    return res;
}

double getCircumference(const Track &track) {
    double res = 0;
    for (size_t i = 0; i + 1 < track.size(); i++) {
        res += (track[i + 1] - track[i]).length();
    }
    return res;
}
double getClosedCircumference(const Track &track) {
    double res = getCircumference(track);
    res += (track[0] - track[track.size() - 1]).length();
    return res;
}

bool cutToClosed(Track &track) {
    auto &points = track.points;
    auto it = points.begin();
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    while (it != points.end() && (points[0] - *it).length() > CLOSED_FIGURE_GAP) {
        it++;
    }
    if (it == points.end()) {
        return false;
    }
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    points.erase(it, points.end());
    return true;
}

double fitsToTrack(const Track &track, const PFigure &figure) {
    BoundingBox box = getBoundingBox(track);
    double maxDistance = TRACK_FIT_GAP;
    maxDistance = std::min(maxDistance, std::min(box.width(), box.height()) * 0.2);

    int goodCount = 0;
    for (Point p : track.points) {
        goodCount += figure->getApproximateDistanceToBorder(p) <= maxDistance;
    }

    return goodCount * 1.0 / track.size();
}

using namespace figures;

bool isDeletionTrack(const Track &track) {
    int cnt = 0;
    std::cout << "new\n";
    for (int i = 0; i < (int)track.size(); i++) {
        int prevPoint = i - 1;
        while (prevPoint >= 0 && (track[i] - track[prevPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            prevPoint--;
        }
        int nextPoint = i + 1;
        while (nextPoint < (int)track.size() && (track[i] - track[nextPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            nextPoint++;
        }
        if (prevPoint >= 0 && nextPoint < (int)track.size()) {
            Point vec1 = track[nextPoint] - track[i];
            Point vec2 = track[prevPoint] - track[i];
            double ang = acos(Point::dotProduct(vec1, vec2) / (vec1.length() * vec2.length()));
            if (ang <= PI * DELETION_MOVE_MAX_ANGLE / 180) {
                cnt++;
                i = nextPoint;
            }
        }
    }
    std::cout << "deletion cnt = " << cnt << "\n";
    return cnt >= DELETION_MOVE_MIN_COUNT;
}

PFigure recognizeConnections(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
        if (!figA) { continue; }
        if (!figure->isInsideOrOnBorder(start)) { continue; }
        for (PFigure figure2 : model) {
            if (figure == figure2) {
                continue;
            }
            auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
            if (figB && figB->isInsideOrOnBorder(end)) {
                auto result = make_shared<SegmentConnection>(figA, figB);
                model.addFigure(result);
                return result;
            }
        }
    }
    return nullptr;
}

PFigure recognizeGrabs(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        if (figure->getApproximateDistanceToBorder(start) <= FIGURE_SELECT_GAP) { // grabbed
            // recognize deletion
            if (isDeletionTrack(track)) {
                model.removeFigure(it);
                return figure;
            }

            // try connection
            auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
            if (figA) {
                for (PFigure figure2 : model) {
                    if (figure == figure2) {
                        continue;
                    }
                    auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
                    if (figB && figB->getApproximateDistanceToBorder(end) <= FIGURE_SELECT_GAP) {
                        auto result = make_shared<SegmentConnection>(figA, figB);
                        model.addFigure(result);
                        return result;
                    }
                }
            }

            // now we try translation
            // but only if figure was selected previously (#65)
            if (figure == model.selectedFigure) {
                figure->translate(end - start);
                model.recalculate();
                return figure;
            }
        }
    }
    return nullptr;
}

void squareBoundedFigure(PBoundedFigure figure) {
    BoundingBox box = figure->getBoundingBox();
    double siz1 = box.width();
    double siz2 = box.height();
    if (siz1 > siz2) {
        std::swap(siz1, siz2);
    }
    std::cout << "bounded size=" << siz1 << ";" << siz2 << "; k=" << (siz1 / siz2) << "\n";
    if (siz1 / siz2 < SQUARING_MIN_RATIO) { return; }

    double siz = (siz1 + siz2) / 2;
    box.rightDown = box.leftUp + Point(siz, siz);
    figure->setBoundingBox(box);
}

struct SelectionFit {
    bool isSegment;
    double distance;
    PFigure figure;

    SelectionFit() : isSegment(false), distance(HUGE_VAL), figure(nullptr) {}
    bool operator<(const SelectionFit &other) const {
        if (isSegment != other.isSegment) { return isSegment > other.isSegment; }
        return distance < other.distance;
    }
};
PFigure recognizeClicks(const Point &click, Model &model) {
    SelectionFit bestFit;
    for (PFigure figure : model) {
        SelectionFit currentFit;
        currentFit.isSegment = !!dynamic_pointer_cast<Segment>(figure);
        currentFit.distance = figure->getApproximateDistanceToBorder(click);
        currentFit.figure = figure;
        if (currentFit.distance >= FIGURE_SELECT_GAP) { continue; }
        bestFit = min(bestFit, currentFit);
    }
    if (bestFit.figure) {
        model.selectedFigure = bestFit.figure;
    } else {
        bool found = false;
        for (PFigure figure : model) {
            if (figure->isInsideOrOnBorder(click)) {
                model.selectedFigure = figure;
                found = true;
            }
        }
        if (!found) {
            model.selectedFigure = nullptr;
        }
    }

    std::shared_ptr<Segment> segm = dynamic_pointer_cast<Segment>(model.selectedFigure);
    if (segm) {
        if ((click - segm->getA()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedA(!segm->getArrowedA());
        }
        if ((click - segm->getB()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedB(!segm->getArrowedB());
        }
        return segm;
    }
    return nullptr;
}

/*
 * Consider a continuos polyline. This function returns its center of mass.
 * Please note that this differs from center of mass of all points of track,
 * we calculate weighted sum of segments' centers (weight == length of segment)
 */
Point getWeightedCenter(const Track &track) {
    Point result;
    double summaryLength = 0;
    for (size_t i = 0; i < track.size(); i++) {
        Point a = track[i];
        Point b = track[(i + 1) % track.size()];
        Point middle = (a + b) * 0.5;
        double length = (b - a).length();
        result += middle * length;
        summaryLength += length;
    }
    result = result * (1.0 / summaryLength);
    return result;
}

/*
 * Calculates best rectangle which will fit the track
 * For example, it considers that there may be some
 * outliners (they happen near corners when drawing on touch devices)
 * First, it calculate approximate center of the rectangle and then
 * it calculates average 'width' and 'height' of the rectangle.
 * Points which differ from the center no more than width/4 are
 * considered 'horizontal segments' and vice-versa for vertical.
 */
BoundingBox getBestFitRectangle(const Track &track) {
    BoundingBox total = getBoundingBox(track);
    double maxDx = total.width() / 4;
    double maxDy = total.height() / 4;
    Point center = getWeightedCenter(track);

    double sumDx = 0, sumDy = 0;
    int countDx = 0, countDy = 0;
    for (Point p : track.points) {
        double dx = fabs(p.x - center.x);
        double dy = fabs(p.y - center.y);
        if (dx < maxDx && dy >= maxDy) {
            sumDy += dy;
            countDy++;
        }
        if (dy < maxDy && dx >= maxDx) {
            sumDx += dx;
            countDx++;
        }
    }
    Point diff(
        countDx ? sumDx / countDx : maxDx,
        countDy ? sumDy / countDy : maxDy
    );
    return BoundingBox({ center - diff, center + diff });
}

std::vector<Point> smoothCurve(std::vector<Point> current) {
    assert(!current.empty());

    Point start = current[0];
    Point end = current.back();
    if ((start - end).length() <= CLOSED_FIGURE_GAP) {
        end = current.back() = start;
    }
    Segment segment(start, end);

    std::pair<double, size_t> maxDistance(0, 0);
    for (size_t i = 0; i < current.size(); i++) {
        double d = segment.getApproximateDistanceToBorder(current[i]);
        maxDistance = max(maxDistance, std::make_pair(d, i));
    }

    std::vector<Point> result;
    if (maxDistance.first > TRACK_FIT_GAP) {
        auto part1 = smoothCurve(std::vector<Point>(current.begin(), current.begin() + maxDistance.second + 1));
        auto part2 = smoothCurve(std::vector<Point>(current.begin() + maxDistance.second, current.end()));
        result.insert(result.end(), part1.begin(), part1.end());
        result.insert(result.end(), part2.begin() + 1, part2.end());
    } else {
        result.push_back(start);
        result.push_back(end);
    }
    return result;
}

PFigure recognize(const Track &_track, Model &model) {
    if (FIGURE_SELECT_GAP < 0 || CLOSED_FIGURE_GAP < 0) {
        throw std::logic_error("Recognition preset is not selected");
    }

    // this is to preserve the API (const Track&) and make future optimizations possible
    // i.e. avoid copying if it's not necessary
    Track track = _track;
    if (track.empty()) { return nullptr; }

    // Very small tracks are clicks
    if (getClosedCircumference(track) <= CLOSED_FIGURE_GAP) {
        return recognizeClicks(track[0], model);
    }

    // Moving and connecting
    if (PFigure result = recognizeGrabs(track, model)) {
        return result;
    }

    // Connecting interior-->interior
    if (PFigure result = recognizeConnections(track, model)) {
        return result;
    }

    // Drawing new figures
    std::vector<PFigure> candidates;
    if (!cutToClosed(track))  {
        candidates.push_back(make_shared<Segment>(track[0], track[track.size() - 1]));
    } else {
        candidates.push_back(make_shared<Ellipse>(getBoundingBox(track)));
        candidates.push_back(make_shared<Rectangle>(getBestFitRectangle(track)));
    }

    if (candidates.empty()) { return nullptr; }

    std::vector<double> fits;
    for (PFigure figure : candidates) {
        fits.push_back(fitsToTrack(track, figure));
    }
    size_t id = max_element(fits.begin(), fits.end()) - fits.begin();
    std::cout << "max_fit = " << fits[id] << "; id = " << id << "\n";
    if (fits[id] >= MIN_FIT_POINTS_AMOUNT) { // we allow some of points to fall out of our track
        auto boundedFigure = dynamic_pointer_cast<BoundedFigure>(candidates[id]);
        if (boundedFigure) {
            squareBoundedFigure(boundedFigure);
        }
        model.addFigure(candidates[id]);
        return candidates[id];
    }

    std::vector<Point> points(track.points.begin(), track.points.end());
    auto result = make_shared<Curve>(smoothCurve(points));
    model.addFigure(result);
    return result;
}
<commit_msg>recognition.fitsToTrack: bug fix horizontal/vertical figures was fixed (maxDistance = max of h/w, not min!)<commit_after>#include "model.h"
#include "recognition.h"
#include <memory>
#include <algorithm>
#include <vector>

using std::min;
using std::max;
using std::make_shared;
using std::dynamic_pointer_cast;

int FIGURE_SELECT_GAP = -1;
int CLOSED_FIGURE_GAP = -1;
const int TRACK_FIT_GAP = 10;
const int DELETION_MOVE_MINIMAL_LENGTH = 5;
const int DELETION_MOVE_MAX_ANGLE = 30;
const int DELETION_MOVE_MIN_COUNT = 3;
const double SQUARING_MIN_RATIO = 0.8;
const double MIN_FIT_POINTS_AMOUNT = 0.75;

void setRecognitionPreset(RecognitionPreset preset) {
    switch (preset) {
    case RecognitionPreset::Mouse:
        FIGURE_SELECT_GAP = 10;
        CLOSED_FIGURE_GAP = 10;
        break;
    case RecognitionPreset::Touch:
        FIGURE_SELECT_GAP = 30;
        CLOSED_FIGURE_GAP = 50;
        break;
    };
}

int figureSelectGap() {
    return FIGURE_SELECT_GAP;
}

BoundingBox getBoundingBox(const Track &track) {
    BoundingBox res;
    res.leftUp = res.rightDown = track[0];
    for (Point p : track.points) {
        res.leftUp.x = min(res.leftUp.x, p.x);
        res.leftUp.y = min(res.leftUp.y, p.y);
        res.rightDown.x = max(res.rightDown.x, p.x);
        res.rightDown.y = max(res.rightDown.y, p.y);
    }
    return res;
}

double getCircumference(const Track &track) {
    double res = 0;
    for (size_t i = 0; i + 1 < track.size(); i++) {
        res += (track[i + 1] - track[i]).length();
    }
    return res;
}
double getClosedCircumference(const Track &track) {
    double res = getCircumference(track);
    res += (track[0] - track[track.size() - 1]).length();
    return res;
}

bool cutToClosed(Track &track) {
    auto &points = track.points;
    auto it = points.begin();
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    while (it != points.end() && (points[0] - *it).length() > CLOSED_FIGURE_GAP) {
        it++;
    }
    if (it == points.end()) {
        return false;
    }
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    points.erase(it, points.end());
    return true;
}

double fitsToTrack(const Track &track, const PFigure &figure) {
    BoundingBox box = getBoundingBox(track);
    double maxDistance = TRACK_FIT_GAP;
    maxDistance = std::min(maxDistance, std::max(box.width(), box.height()) * 0.2);

    int goodCount = 0;
    for (Point p : track.points) {
        goodCount += figure->getApproximateDistanceToBorder(p) <= maxDistance;
    }

    return goodCount * 1.0 / track.size();
}

using namespace figures;

bool isDeletionTrack(const Track &track) {
    int cnt = 0;
    std::cout << "new\n";
    for (int i = 0; i < (int)track.size(); i++) {
        int prevPoint = i - 1;
        while (prevPoint >= 0 && (track[i] - track[prevPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            prevPoint--;
        }
        int nextPoint = i + 1;
        while (nextPoint < (int)track.size() && (track[i] - track[nextPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            nextPoint++;
        }
        if (prevPoint >= 0 && nextPoint < (int)track.size()) {
            Point vec1 = track[nextPoint] - track[i];
            Point vec2 = track[prevPoint] - track[i];
            double ang = acos(Point::dotProduct(vec1, vec2) / (vec1.length() * vec2.length()));
            if (ang <= PI * DELETION_MOVE_MAX_ANGLE / 180) {
                cnt++;
                i = nextPoint;
            }
        }
    }
    std::cout << "deletion cnt = " << cnt << "\n";
    return cnt >= DELETION_MOVE_MIN_COUNT;
}

PFigure recognizeConnections(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
        if (!figA) { continue; }
        if (!figure->isInsideOrOnBorder(start)) { continue; }
        for (PFigure figure2 : model) {
            if (figure == figure2) {
                continue;
            }
            auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
            if (figB && figB->isInsideOrOnBorder(end)) {
                auto result = make_shared<SegmentConnection>(figA, figB);
                model.addFigure(result);
                return result;
            }
        }
    }
    return nullptr;
}

PFigure recognizeGrabs(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        if (figure->getApproximateDistanceToBorder(start) <= FIGURE_SELECT_GAP) { // grabbed
            // recognize deletion
            if (isDeletionTrack(track)) {
                model.removeFigure(it);
                return figure;
            }

            // try connection
            auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
            if (figA) {
                for (PFigure figure2 : model) {
                    if (figure == figure2) {
                        continue;
                    }
                    auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
                    if (figB && figB->getApproximateDistanceToBorder(end) <= FIGURE_SELECT_GAP) {
                        auto result = make_shared<SegmentConnection>(figA, figB);
                        model.addFigure(result);
                        return result;
                    }
                }
            }

            // now we try translation
            // but only if figure was selected previously (#65)
            if (figure == model.selectedFigure) {
                figure->translate(end - start);
                model.recalculate();
                return figure;
            }
        }
    }
    return nullptr;
}

void squareBoundedFigure(PBoundedFigure figure) {
    BoundingBox box = figure->getBoundingBox();
    double siz1 = box.width();
    double siz2 = box.height();
    if (siz1 > siz2) {
        std::swap(siz1, siz2);
    }
    std::cout << "bounded size=" << siz1 << ";" << siz2 << "; k=" << (siz1 / siz2) << "\n";
    if (siz1 / siz2 < SQUARING_MIN_RATIO) { return; }

    double siz = (siz1 + siz2) / 2;
    box.rightDown = box.leftUp + Point(siz, siz);
    figure->setBoundingBox(box);
}

struct SelectionFit {
    bool isSegment;
    double distance;
    PFigure figure;

    SelectionFit() : isSegment(false), distance(HUGE_VAL), figure(nullptr) {}
    bool operator<(const SelectionFit &other) const {
        if (isSegment != other.isSegment) { return isSegment > other.isSegment; }
        return distance < other.distance;
    }
};
PFigure recognizeClicks(const Point &click, Model &model) {
    SelectionFit bestFit;
    for (PFigure figure : model) {
        SelectionFit currentFit;
        currentFit.isSegment = !!dynamic_pointer_cast<Segment>(figure);
        currentFit.distance = figure->getApproximateDistanceToBorder(click);
        currentFit.figure = figure;
        if (currentFit.distance >= FIGURE_SELECT_GAP) { continue; }
        bestFit = min(bestFit, currentFit);
    }
    if (bestFit.figure) {
        model.selectedFigure = bestFit.figure;
    } else {
        bool found = false;
        for (PFigure figure : model) {
            if (figure->isInsideOrOnBorder(click)) {
                model.selectedFigure = figure;
                found = true;
            }
        }
        if (!found) {
            model.selectedFigure = nullptr;
        }
    }

    std::shared_ptr<Segment> segm = dynamic_pointer_cast<Segment>(model.selectedFigure);
    if (segm) {
        if ((click - segm->getA()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedA(!segm->getArrowedA());
        }
        if ((click - segm->getB()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedB(!segm->getArrowedB());
        }
        return segm;
    }
    return nullptr;
}

/*
 * Consider a continuos polyline. This function returns its center of mass.
 * Please note that this differs from center of mass of all points of track,
 * we calculate weighted sum of segments' centers (weight == length of segment)
 */
Point getWeightedCenter(const Track &track) {
    Point result;
    double summaryLength = 0;
    for (size_t i = 0; i < track.size(); i++) {
        Point a = track[i];
        Point b = track[(i + 1) % track.size()];
        Point middle = (a + b) * 0.5;
        double length = (b - a).length();
        result += middle * length;
        summaryLength += length;
    }
    result = result * (1.0 / summaryLength);
    return result;
}

/*
 * Calculates best rectangle which will fit the track
 * For example, it considers that there may be some
 * outliners (they happen near corners when drawing on touch devices)
 * First, it calculate approximate center of the rectangle and then
 * it calculates average 'width' and 'height' of the rectangle.
 * Points which differ from the center no more than width/4 are
 * considered 'horizontal segments' and vice-versa for vertical.
 */
BoundingBox getBestFitRectangle(const Track &track) {
    BoundingBox total = getBoundingBox(track);
    double maxDx = total.width() / 4;
    double maxDy = total.height() / 4;
    Point center = getWeightedCenter(track);

    double sumDx = 0, sumDy = 0;
    int countDx = 0, countDy = 0;
    for (Point p : track.points) {
        double dx = fabs(p.x - center.x);
        double dy = fabs(p.y - center.y);
        if (dx < maxDx && dy >= maxDy) {
            sumDy += dy;
            countDy++;
        }
        if (dy < maxDy && dx >= maxDx) {
            sumDx += dx;
            countDx++;
        }
    }
    Point diff(
        countDx ? sumDx / countDx : maxDx,
        countDy ? sumDy / countDy : maxDy
    );
    return BoundingBox({ center - diff, center + diff });
}

std::vector<Point> smoothCurve(std::vector<Point> current) {
    assert(!current.empty());

    Point start = current[0];
    Point end = current.back();
    if ((start - end).length() <= CLOSED_FIGURE_GAP) {
        end = current.back() = start;
    }
    Segment segment(start, end);

    std::pair<double, size_t> maxDistance(0, 0);
    for (size_t i = 0; i < current.size(); i++) {
        double d = segment.getApproximateDistanceToBorder(current[i]);
        maxDistance = max(maxDistance, std::make_pair(d, i));
    }

    std::vector<Point> result;
    if (maxDistance.first > TRACK_FIT_GAP) {
        auto part1 = smoothCurve(std::vector<Point>(current.begin(), current.begin() + maxDistance.second + 1));
        auto part2 = smoothCurve(std::vector<Point>(current.begin() + maxDistance.second, current.end()));
        result.insert(result.end(), part1.begin(), part1.end());
        result.insert(result.end(), part2.begin() + 1, part2.end());
    } else {
        result.push_back(start);
        result.push_back(end);
    }
    return result;
}

PFigure recognize(const Track &_track, Model &model) {
    if (FIGURE_SELECT_GAP < 0 || CLOSED_FIGURE_GAP < 0) {
        throw std::logic_error("Recognition preset is not selected");
    }

    // this is to preserve the API (const Track&) and make future optimizations possible
    // i.e. avoid copying if it's not necessary
    Track track = _track;
    if (track.empty()) { return nullptr; }

    // Very small tracks are clicks
    if (getClosedCircumference(track) <= CLOSED_FIGURE_GAP) {
        return recognizeClicks(track[0], model);
    }

    // Moving and connecting
    if (PFigure result = recognizeGrabs(track, model)) {
        return result;
    }

    // Connecting interior-->interior
    if (PFigure result = recognizeConnections(track, model)) {
        return result;
    }

    // Drawing new figures
    std::vector<PFigure> candidates;
    if (!cutToClosed(track))  {
        candidates.push_back(make_shared<Segment>(track[0], track[track.size() - 1]));
    } else {
        candidates.push_back(make_shared<Ellipse>(getBoundingBox(track)));
        candidates.push_back(make_shared<Rectangle>(getBestFitRectangle(track)));
    }

    if (candidates.empty()) { return nullptr; }

    std::vector<double> fits;
    for (PFigure figure : candidates) {
        fits.push_back(fitsToTrack(track, figure));
    }
    size_t id = max_element(fits.begin(), fits.end()) - fits.begin();
    std::cout << "max_fit = " << fits[id] << "; id = " << id << "\n";
    if (fits[id] >= MIN_FIT_POINTS_AMOUNT) { // we allow some of points to fall out of our track
        auto boundedFigure = dynamic_pointer_cast<BoundedFigure>(candidates[id]);
        if (boundedFigure) {
            squareBoundedFigure(boundedFigure);
        }
        model.addFigure(candidates[id]);
        return candidates[id];
    }

    std::vector<Point> points(track.points.begin(), track.points.end());
    auto result = make_shared<Curve>(smoothCurve(points));
    model.addFigure(result);
    return result;
}
<|endoftext|>


<commit_before>/**
Copyright (c) 2016, Philip Deegan.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
    * Neither the name of Philip Deegan nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _KUL_GIT_GIST_HPP_
#define _KUL_GIT_GIST_HPP_

//
// REQUIRES parse.json
//

#include "kul/io.hpp"
#include "kul/https.hpp"

#include <json/reader.h>
#include <json/writer.h>

namespace kul{ namespace github{

class Exception : public kul::Exception{
    public:
        Exception(const char*f, const uint16_t& l, const std::string& s) : kul::Exception(f, l, s){}
};

class JsonResponse{
    private:
        Json::Value j;
        bool f = 0;
    public:
        void handle(const kul::hash::map::S2S& h, const std::string& b){
            std::vector<std::string> lines;
            kul::String::LINES(b, lines);
            std::string json = lines.size() == 1 ? b : lines[1];
            Json::Reader reader;
            f = !reader.parse(json.c_str(), j);
        }
        bool fail(){ return f; }
        const Json::Value& json(){ return j; }
};

class JsonGet : public kul::https::_1_1GetRequest, public JsonResponse{
    public:
        JsonGet(){
            header("Accept", "application/json");
        }
        void handle(const kul::hash::map::S2S& h, const std::string& b){ JsonResponse::handle(h, b); }
};
class JsonPost : public kul::https::_1_1PostRequest, public JsonResponse{
    public:
        JsonPost(){
            header("Accept", "application/json");
        }
        void handle(const kul::hash::map::S2S& h, const std::string& b){ JsonResponse::handle(h, b); }
};

class Gist{
    public:
        static std::string CREATE(const std::string& ua, const std::string& d, const std::vector<kul::File>& fs, bool pub = 0) throw(Exception) {
            Json::Value json;
            json["description"] = d;
            if(pub) json["public"] = true;
            Json::Value files;
            for(const auto& f : fs){
                std::stringstream ss;
                const std::string* s = 0;
                kul::io::Reader r(f);
                while((s = r.readLine())) ss << *s;
                Json::Value content;
                content["content"] = ss.str();
                files[f.name()] = content;
            }
            json["files"] = files;
            std::stringstream ss;
            ss << Json::FastWriter().write(json);
            JsonPost p;
            p.header("User-Agent", ua);
            p.body(ss.str());
            p.send("api.github.com", "gists");
            if(p.fail()) KEXCEPTION("Post JSON response failed to parse");
            return p.json()["id"].asString();
        }
};

}// END NAMESPACE github
}// END NAMESPACE kul

#endif /* _KUL_GIT_GIST_HPP_ */<commit_msg>inline with kul io changes<commit_after>/**
Copyright (c) 2016, Philip Deegan.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
    * Neither the name of Philip Deegan nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _KUL_GIT_GIST_HPP_
#define _KUL_GIT_GIST_HPP_

//
// REQUIRES parse.json
//

#include "kul/io.hpp"
#include "kul/https.hpp"

#include <json/reader.h>
#include <json/writer.h>

namespace kul{ namespace github{

class Exception : public kul::Exception{
    public:
        Exception(const char*f, const uint16_t& l, const std::string& s) : kul::Exception(f, l, s){}
};

class JsonResponse{
    private:
        Json::Value j;
        bool f = 0;
    public:
        void handle(const kul::hash::map::S2S& h, const std::string& b){
            std::vector<std::string> lines;
            kul::String::LINES(b, lines);
            std::string json = lines.size() == 1 ? b : lines[1];
            Json::Reader reader;
            f = !reader.parse(json.c_str(), j);
        }
        bool fail(){ return f; }
        const Json::Value& json(){ return j; }
};

class JsonGet : public kul::https::_1_1GetRequest, public JsonResponse{
    public:
        JsonGet(){
            header("Accept", "application/json");
        }
        void handle(const kul::hash::map::S2S& h, const std::string& b){ JsonResponse::handle(h, b); }
};
class JsonPost : public kul::https::_1_1PostRequest, public JsonResponse{
    public:
        JsonPost(){
            header("Accept", "application/json");
        }
        void handle(const kul::hash::map::S2S& h, const std::string& b){ JsonResponse::handle(h, b); }
};

class Gist{
    public:
        static std::string CREATE(const std::string& ua, const std::string& d, const std::vector<kul::File>& fs, bool pub = 0) throw(Exception) {
            Json::Value json;
            json["description"] = d;
            if(pub) json["public"] = true;
            Json::Value files;
            for(const auto& f : fs){
                std::stringstream ss;
                const char* s = 0;
                kul::io::Reader r(f);
                while((s = r.readLine())) ss << *s;
                Json::Value content;
                content["content"] = ss.str();
                files[f.name()] = content;
            }
            json["files"] = files;
            std::stringstream ss;
            ss << Json::FastWriter().write(json);
            JsonPost p;
            p.header("User-Agent", ua);
            p.body(ss.str());
            p.send("api.github.com", "gists");
            if(p.fail()) KEXCEPTION("Post JSON response failed to parse");
            return p.json()["id"].asString();
        }
};

}// END NAMESPACE github
}// END NAMESPACE kul

#endif /* _KUL_GIT_GIST_HPP_ */<|endoftext|>


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

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

#include <assert.h>
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

namespace {

bool isDigit(char16_t c)
{
    return '0' <= c && c <= '9';
}

int isHexDigit(char16_t c)
{
    if ('0' <= c && c <= '9')
        return '0';
    if ('A' <= c && c <= 'F')
        return 'A' - 10;
    if ('a' <= c && c <= 'f')
        return 'a' - 10;
    return 0;
}

bool isSpace(char16_t c)
{
    switch (c) {
    // White Space
    case u'\u0009': // Tab <TAB>
    case u'\u000B': // Vertical Tab <VT>
    case u'\u000C': // Form Feed <FF>
    case u'\u0020': // Space <SP>
    case u'\u00A0': // No-break space <NBSP>
    case u'\uFEFF': // Byte Order Mark <BOM>
    // Other category “Zs” Any other Unicode “space separator” <USP>
    case u'\u1680':
    case u'\u180e':
    case u'\u2000':
    case u'\u2001':
    case u'\u2002':
    case u'\u2003':
    case u'\u2004':
    case u'\u2005':
    case u'\u2006':
    case u'\u2007':
    case u'\u2008':
    case u'\u2009':
    case u'\u200a':
    case u'\u202f':
    case u'\u205f':
    case u'\u3000':
    // Line Terminators
    case u'\u000A':  // Line Feed <LF>
    case u'\u000D':  // Carriage Return <CR>
    case u'\u2028':  // Line separator <LS>
    case u'\u2029':  // Paragraph separator <PS>
        return true;
    default:
        return false;
    }
}

std::u16string toString(const char* format, ...)
{
    va_list ap;
    char buffer[24];
    std::u16string text;

    va_start(ap, format);
    vsnprintf(buffer, sizeof buffer, format, ap);
    va_end(ap);
    for (char* p = buffer; *p; ++p)
        text += static_cast<char16_t>(*p);
    return text;
}

std::u16string toString(double value)
{
    if (isnan(value))
        return u"NaN";
    if (isinf(value))
        return (isinf(value) == 1) ? u"Infinity" : u"-Infinity";
    std::u16string text;
    char buffer[24];
    std::sprintf(buffer, "%g", value);
    for (char* p = buffer; *p; ++p)
        text += static_cast<char16_t>(*p);
    return text;
}

double toNumber(const std::u16string& value)
{
    char16_t c = 0;
    std::u16string::const_iterator i = value.begin();
    while (i != value.end()) {
        c = *i;
        if (!isSpace(c))
            break;
        ++i;
    }
    double number = 0.0;
    if (value.compare(i - value.begin(), 2, u"0x") == 0 || value.compare(i - value.begin(), 2, u"0X") == 0) {
        for (i += 2; i != value.end(); ++i) {
            c = *i;
            int hex = isHexDigit(c);
            if (!hex)
                break;
            number *= 16;
            number += (c - hex);
        }
    } else {
        bool negative = (c == '-');
        if (negative || c == '+')
            ++i;
        int exp = 0;
        if (value.compare(i - value.begin(), 8, u"Infinity") == 0) {
            i += 8;
            number = negative ? -INFINITY : INFINITY;
        } else {
            while (i != value.end()) {
                c = *i;
                if (!isDigit(c))
                    break;
                number *= 10.0;
                number += (c - '0');
                ++i;
            }
            if (c == '.') {
                while (++i != value.end()) {
                    c = *i;
                    if (!isDigit(c))
                        break;
                    number *= 10.0;
                    number += (c - '0');
                    --exp;
                }
            }
            if (negative)
                number = -number;
            if (c == 'e' || c == 'E') {
                negative = false;
                if (++i != value.end()) {
                    c = *i;
                    negative = (c == '-');
                    if (negative || c == '+')
                        ++i;
                }
                int n = 0;
                while (i != value.end()) {
                    c = *i;
                    if (!isDigit(c))
                        break;
                    n *= 10;
                    n += (c - '0');
                    ++i;
                }
                if (negative)
                    exp -= n;
                else
                    exp += n;
            }
            if (DBL_MAX_EXP < exp)
                number = negative ? -INFINITY : INFINITY;
            else if (exp < DBL_MIN_EXP)
                number = negative ? -0.0 : 0.0;
            else {
                double p10 = 10.0;
                int n = exp;
                if (n < 0)
                    n = -n;
                while (n) {
                    if (n & 1) {
                        if (exp < 0)
                            number /= p10;
                        else
                            number *= p10;
                    }
                    n >>= 1;
                    p10 *= p10;
                }
            }
        }
    }
    while (i != value.end()) {
        if (!isSpace(*i))
            return NAN;
        ++i;
    }
    return number;
};

}  // namespace

bool Any::isString() const
{
    return (type == Dynamic) && vtable->getType() == typeid(std::u16string);
}

std::u16string Any::toString() const
{
    switch (type) {
    case Bool:
        return heap.i32 ? u"true" : u"false";
    case Int32:
        return ::toString("%d", heap.i32);
    case Uint32:
        return ::toString("%u", heap.u32);
    case Int64:
        return ::toString("%lld", heap.i64);
    case Uint64:
        return ::toString("%llu", heap.u64);
    case Float32:
        return ::toString(heap.f32);
    case Float64:
        return ::toString(heap.f64);
    case Dynamic:
        if (vtable->getType() == typeid(std::u16string))
            return *reinterpret_cast<const std::u16string*>(&heap);
    default:
        break;
    }
    return std::u16string();
}

// toNumber
// for Object, call valueOf and then apply toNumber
// null returns 0, undefined returns NAN
double Any::toNumber() const
{
    switch (type) {
    case Undefined:
        return NAN;
    case Bool:
    case Int32:
        return heap.i32;
    case Uint32:
        return heap.u32;
    case Int64:
        return heap.i64;
    case Uint64:
        return heap.u64;
    case Float32:
        return heap.f32;
    case Float64:
        return heap.f64;
    case Dynamic:
        if (vtable->getType() == typeid(std::u16string))
            return ::toNumber(*reinterpret_cast<const std::u16string*>(&heap));
        break;
    default:
        break;
    }
    return 0.0;
}

// toBoolean
// Object returns true. Otherwise, false.
// NAN returns false.
// empty string returns false. Otherwise, true.
bool Any::toBoolean() const
{
    switch (type) {
    case Bool:
    case Int32:
        return heap.i32;
    case Uint32:
        return heap.u32;
    case Int64:
        return heap.i64;
    case Uint64:
        return heap.u64;
    case Float32:
        return heap.f32 && !isnan(heap.f32);
    case Float64:
        return heap.f64 && !isnan(heap.f64);
    case Dynamic:
        if (vtable->getType() == typeid(std::u16string))
            return (*reinterpret_cast<const std::u16string*>(&heap)).length();
        return true;
        break;
    default:
        break;
    }
    return false;
}

bool Any::isObject() const
{
    return (type == Dynamic) && vtable->getType() == typeid(Object);
}

Object* Any::toObject() const
{
    if (isObject())
        return reinterpret_cast<const Object*>(&heap)->self();
    return 0;
}<commit_msg>(Any) : Merge changes in esidl/jsapi.<commit_after>/*
 * Copyright 2011 Esrille Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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

#include <assert.h>
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

namespace {

bool isDigit(char16_t c)
{
    return '0' <= c && c <= '9';
}

int isHexDigit(char16_t c)
{
    if ('0' <= c && c <= '9')
        return '0';
    if ('A' <= c && c <= 'F')
        return 'A' - 10;
    if ('a' <= c && c <= 'f')
        return 'a' - 10;
    return 0;
}

bool isSpace(char16_t c)
{
    switch (c) {
    // White Space
    case u'\u0009': // Tab <TAB>
    case u'\u000B': // Vertical Tab <VT>
    case u'\u000C': // Form Feed <FF>
    case u'\u0020': // Space <SP>
    case u'\u00A0': // No-break space <NBSP>
    case u'\uFEFF': // Byte Order Mark <BOM>
    // Other category “Zs” Any other Unicode “space separator” <USP>
    case u'\u1680':
    case u'\u180e':
    case u'\u2000':
    case u'\u2001':
    case u'\u2002':
    case u'\u2003':
    case u'\u2004':
    case u'\u2005':
    case u'\u2006':
    case u'\u2007':
    case u'\u2008':
    case u'\u2009':
    case u'\u200a':
    case u'\u202f':
    case u'\u205f':
    case u'\u3000':
    // Line Terminators
    case u'\u000A':  // Line Feed <LF>
    case u'\u000D':  // Carriage Return <CR>
    case u'\u2028':  // Line separator <LS>
    case u'\u2029':  // Paragraph separator <PS>
        return true;
    default:
        return false;
    }
}

std::u16string toString(const char* format, ...)
{
    va_list ap;
    char buffer[24];
    std::u16string text;

    va_start(ap, format);
    vsnprintf(buffer, sizeof buffer, format, ap);
    va_end(ap);
    for (char* p = buffer; *p; ++p)
        text += static_cast<char16_t>(*p);
    return text;
}

std::u16string toString(double value)
{
    if (isnan(value))
        return u"NaN";
    if (isinf(value))
        return (isinf(value) == 1) ? u"Infinity" : u"-Infinity";
    std::u16string text;
    char buffer[24];
    std::sprintf(buffer, "%g", value);
    for (char* p = buffer; *p; ++p)
        text += static_cast<char16_t>(*p);
    return text;
}

std::u16string toString(Object* object)
{
    if (!object)
        return u"null";
    Any result = object->message_(0, 0, Object::STRINGIFY_, 0);
    if (result.isString())
        return result.toString();
    return u"";
}

double toNumber(const std::u16string& value)
{
    char16_t c = 0;
    std::u16string::const_iterator i = value.begin();
    while (i != value.end()) {
        c = *i;
        if (!isSpace(c))
            break;
        ++i;
    }
    double number = 0.0;
    if (value.compare(i - value.begin(), 2, u"0x") == 0 || value.compare(i - value.begin(), 2, u"0X") == 0) {
        for (i += 2; i != value.end(); ++i) {
            c = *i;
            int hex = isHexDigit(c);
            if (!hex)
                break;
            number *= 16;
            number += (c - hex);
        }
    } else {
        bool negative = (c == '-');
        if (negative || c == '+')
            ++i;
        int exp = 0;
        if (value.compare(i - value.begin(), 8, u"Infinity") == 0) {
            i += 8;
            number = negative ? -INFINITY : INFINITY;
        } else {
            while (i != value.end()) {
                c = *i;
                if (!isDigit(c))
                    break;
                number *= 10.0;
                number += (c - '0');
                ++i;
            }
            if (c == '.') {
                while (++i != value.end()) {
                    c = *i;
                    if (!isDigit(c))
                        break;
                    number *= 10.0;
                    number += (c - '0');
                    --exp;
                }
            }
            if (negative)
                number = -number;
            if (c == 'e' || c == 'E') {
                negative = false;
                if (++i != value.end()) {
                    c = *i;
                    negative = (c == '-');
                    if (negative || c == '+')
                        ++i;
                }
                int n = 0;
                while (i != value.end()) {
                    c = *i;
                    if (!isDigit(c))
                        break;
                    n *= 10;
                    n += (c - '0');
                    ++i;
                }
                if (negative)
                    exp -= n;
                else
                    exp += n;
            }
            if (DBL_MAX_EXP < exp)
                number = negative ? -INFINITY : INFINITY;
            else if (exp < DBL_MIN_EXP)
                number = negative ? -0.0 : 0.0;
            else {
                double p10 = 10.0;
                int n = exp;
                if (n < 0)
                    n = -n;
                while (n) {
                    if (n & 1) {
                        if (exp < 0)
                            number /= p10;
                        else
                            number *= p10;
                    }
                    n >>= 1;
                    p10 *= p10;
                }
            }
        }
    }
    while (i != value.end()) {
        if (!isSpace(*i))
            return NAN;
        ++i;
    }
    return number;
};

}  // namespace

bool Any::isString() const
{
    return (type == Dynamic) && vtable->getType() == typeid(std::u16string);
}

std::u16string Any::toString() const
{
    switch (type) {
    case Bool:
        return heap.i32 ? u"true" : u"false";
    case Int32:
        return ::toString("%d", heap.i32);
    case Uint32:
        return ::toString("%u", heap.u32);
    case Int64:
        return ::toString("%lld", heap.i64);
    case Uint64:
        return ::toString("%llu", heap.u64);
    case Float32:
        return ::toString(heap.f32);
    case Float64:
        return ::toString(heap.f64);
    case Dynamic:
        if (vtable->getType() == typeid(std::u16string))
            return *reinterpret_cast<const std::u16string*>(&heap);
        if (vtable->getType() == typeid(Object))
            return ::toString(const_cast<Object*>(reinterpret_cast<const Object*>(&heap)->self()));
        break;
    default:
        break;
    }
    return std::u16string();
}

// toNumber
// for Object, call valueOf and then apply toNumber
// null returns 0, undefined returns NAN
double Any::toNumber() const
{
    switch (type) {
    case Undefined:
        return NAN;
    case Bool:
    case Int32:
        return heap.i32;
    case Uint32:
        return heap.u32;
    case Int64:
        return heap.i64;
    case Uint64:
        return heap.u64;
    case Float32:
        return heap.f32;
    case Float64:
        return heap.f64;
    case Dynamic:
        if (vtable->getType() == typeid(std::u16string))
            return ::toNumber(*reinterpret_cast<const std::u16string*>(&heap));
        break;
    default:
        break;
    }
    return 0.0;
}

// toBoolean
// Object returns true. Otherwise, false.
// NAN returns false.
// empty string returns false. Otherwise, true.
bool Any::toBoolean() const
{
    switch (type) {
    case Bool:
    case Int32:
        return heap.i32;
    case Uint32:
        return heap.u32;
    case Int64:
        return heap.i64;
    case Uint64:
        return heap.u64;
    case Float32:
        return heap.f32 && !isnan(heap.f32);
    case Float64:
        return heap.f64 && !isnan(heap.f64);
    case Dynamic:
        if (vtable->getType() == typeid(std::u16string))
            return (*reinterpret_cast<const std::u16string*>(&heap)).length();
        return true;
        break;
    default:
        break;
    }
    return false;
}

bool Any::isObject() const
{
    return (type == Dynamic) && vtable->getType() == typeid(Object);
}

Object* Any::toObject() const
{
    if (isObject())
        return reinterpret_cast<const Object*>(&heap)->self();
    return 0;
}<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "cantera/thermo/FixedChemPotSSTP.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/thermo/SpeciesThermoFactory.h"
#include "cantera/thermo/speciesThermoTypes.h"
#include "cantera/thermo/IdealGasPhase.h"
#include "cantera/base/ctml.h"
#include <fstream>

namespace Cantera
{

class FixedChemPotSstpConstructorTest : public testing::Test
{
};

TEST_F(FixedChemPotSstpConstructorTest, fromXML)
{
    ThermoPhase* p = newPhase("../data/LiFixed.xml");
    ASSERT_EQ((int) p->nSpecies(), 1);
    double mu;
    p->getChemPotentials(&mu);
    ASSERT_FLOAT_EQ(-2.3e7, mu);
    delete p;
}

TEST_F(FixedChemPotSstpConstructorTest, SimpleConstructor)
{
    FixedChemPotSSTP p("Li", -2.3e7);
    ASSERT_EQ((int) p.nSpecies(), 1);
    double mu;
    p.getChemPotentials(&mu);
    ASSERT_FLOAT_EQ(-2.3e7, mu);
}

#ifndef HAS_NO_PYTHON // skip these tests if the Python converter is unavailable
class CtiConversionTest : public testing::Test
{
public:
    CtiConversionTest() : p1(0), p2(0) {
        appdelete();
    }
    ~CtiConversionTest() {
        delete p1;
        delete p2;
    }

    ThermoPhase* p1;
    ThermoPhase* p2;
    void compare()
    {
        ASSERT_EQ(p1->nSpecies(), p2->nSpecies());
        for (size_t i = 0; i < p1->nSpecies(); i++) {
            ASSERT_EQ(p1->speciesName(i), p2->speciesName(i));
            ASSERT_EQ(p1->molecularWeight(i), p2->molecularWeight(i));
        }
    }
};

TEST_F(CtiConversionTest, ExplicitConversion) {
    p1 = newPhase("../data/air-no-reactions.xml");
    ctml::ct2ctml("../data/air-no-reactions.cti");
    p2 = newPhase("air-no-reactions.xml", "");
    compare();
}

TEST_F(CtiConversionTest, ImplicitConversion) {
    p1 = newPhase("../data/air-no-reactions.xml");
    p2 = newPhase("../data/air-no-reactions.cti");
    compare();
}

class ChemkinConversionTest : public testing::Test {
public:
    void copyInputFile(const std::string& name) {
        std::string in_name = "../data/" + name;
        std::ifstream source(in_name.c_str(), std::ios::binary);
        std::ofstream dest(name.c_str(), std::ios::binary);
        dest << source.rdbuf();
    }
};

TEST_F(ChemkinConversionTest, ValidConversion) {
    copyInputFile("pdep-test.inp");
    ctml::ck2cti("pdep-test.inp");
    ThermoPhase* p = newPhase("pdep-test.cti");
    ASSERT_GT(p->temperature(), 0.0);
    delete p;
}

TEST_F(ChemkinConversionTest, MissingInputFile) {
    ASSERT_THROW(ctml::ck2cti("nonexistent-file.inp"),
                 CanteraError);
}

TEST_F(ChemkinConversionTest, FailedConversion) {
    copyInputFile("h2o2_missingThermo.inp");
    ASSERT_THROW(ctml::ck2cti("h2o2_missingThermo.inp"),
                 CanteraError);
}
#endif

// 2-region NASA coefficients; Order is significantly different from the
// standard NASA format.
double h2o_coeffs[] = {
    1000.0, -3.029372670E+04, -8.490322080E-01, 4.198640560E+00,
    -2.036434100E-03, 6.520402110E-06, -5.487970620E-09, 1.771978170E-12,
    -3.000429710E+04, 4.966770100E+00,  3.033992490E+00, 2.176918040E-03,
    -1.640725180E-07, -9.704198700E-11, 1.682009920E-14};
double h2o_comp[] = {2.0, 1.0, 0.0};

double h2_coeffs[] = {
    1000.0, -9.17935173E+02, 6.83010238E-01, 2.34433112E+00,
    7.98052075E-03, -1.94781510E-05, 2.01572094E-08, -7.37611761E-12,
    -9.50158922E+02, -3.20502331E+00, 3.33727920E+00, -4.94024731E-05,
    4.99456778E-07, -1.79566394E-10, 2.00255376E-14};
double h2_comp[] = {2.0, 0.0, 0.0};

double o2_coeffs[] = {
    1000.0, -1.063943560E+03, 3.657675730E+00, 3.782456360E+00,
    -2.996734160E-03, 9.847302010E-06, -9.681295090E-09, 3.243728370E-12,
    -1.088457720E+03, 5.453231290E+00, 3.282537840E+00, 1.483087540E-03,
    -7.579666690E-07, 2.094705550E-10, -2.167177940E-14};
double o2_comp[] = {0.0, 2.0, 0.0};

double oh_coeffs[] = {
    1000.0, 3.615080560E+03, -1.039254580E-01, 3.992015430E+00,
    -2.401317520E-03, 4.617938410E-06, -3.881133330E-09, 1.364114700E-12,
    3.858657000E+03, 4.476696100E+00, 3.092887670E+00, 5.484297160E-04,
    1.265052280E-07, -8.794615560E-11, 1.174123760E-14};
double oh_comp[] = {1.0, 1.0, 0.0};

// 2-region Shomate coefficients
double co2_coeffs[] = {
    1200.0, 24.99735, 55.18696, -33.69137, 7.948387, -0.136638, -403.6075, 228.2431,
    58.16639, 2.720074, -0.492289, 0.038844, -6.447293, -425.9186, 263.6125};
double co2_comp[] = {0.0, 2.0, 1.0};

class ConstructFromScratch : public testing::Test
{
public:
    ConstructFromScratch() {
        p.addElement("H");
        p.addElement("O");
        p.addElement("C");
    }
    IdealGasPhase p;
};

TEST_F(ConstructFromScratch, AddElements)
{
    ASSERT_EQ(3, p.nElements());
    ASSERT_EQ("H", p.elementName(0));
    ASSERT_EQ(1, p.elementIndex("O"));
}

TEST_F(ConstructFromScratch, AddSpeciesNasa)
{
    p.setSpeciesThermo(newSpeciesThermoMgr(NASA));
    SpeciesThermo& sp = p.speciesThermo();

    p.addUniqueSpecies("H2O", h2o_comp);
    sp.install("H2O", 0, NASA, h2o_coeffs, 200.0, 3500.0, 101325.0);
    p.addUniqueSpecies("H2", h2_comp);
    sp.install("H2", 1, NASA, h2_coeffs, 200.0, 3500.0, 101325.0);

    ASSERT_EQ(2, p.nSpecies());

    p.addUniqueSpecies("O2", o2_comp);
    sp.install("O2", 2, NASA, o2_coeffs, 200.0, 3500.0, 101325.0);
    p.addUniqueSpecies("OH", oh_comp);
    sp.install("OH", 3, NASA, oh_coeffs, 200.0, 3500.0, 101325.0);

    ASSERT_EQ(4, p.nSpecies());
    ASSERT_EQ("H2", p.speciesName(1));
    ASSERT_EQ(2, p.nAtoms(2, 1)); // O in O2
    ASSERT_EQ(2, p.nAtoms(0, 0)); // H in H2O
}

} // namespace Cantera
<commit_msg>[Test] fix some size_t warnings<commit_after>#include "gtest/gtest.h"
#include "cantera/thermo/FixedChemPotSSTP.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/thermo/SpeciesThermoFactory.h"
#include "cantera/thermo/speciesThermoTypes.h"
#include "cantera/thermo/IdealGasPhase.h"
#include "cantera/base/ctml.h"
#include <fstream>

namespace Cantera
{

class FixedChemPotSstpConstructorTest : public testing::Test
{
};

TEST_F(FixedChemPotSstpConstructorTest, fromXML)
{
    ThermoPhase* p = newPhase("../data/LiFixed.xml");
    ASSERT_EQ((int) p->nSpecies(), 1);
    double mu;
    p->getChemPotentials(&mu);
    ASSERT_FLOAT_EQ(-2.3e7, mu);
    delete p;
}

TEST_F(FixedChemPotSstpConstructorTest, SimpleConstructor)
{
    FixedChemPotSSTP p("Li", -2.3e7);
    ASSERT_EQ((int) p.nSpecies(), 1);
    double mu;
    p.getChemPotentials(&mu);
    ASSERT_FLOAT_EQ(-2.3e7, mu);
}

#ifndef HAS_NO_PYTHON // skip these tests if the Python converter is unavailable
class CtiConversionTest : public testing::Test
{
public:
    CtiConversionTest() : p1(0), p2(0) {
        appdelete();
    }
    ~CtiConversionTest() {
        delete p1;
        delete p2;
    }

    ThermoPhase* p1;
    ThermoPhase* p2;
    void compare()
    {
        ASSERT_EQ(p1->nSpecies(), p2->nSpecies());
        for (size_t i = 0; i < p1->nSpecies(); i++) {
            ASSERT_EQ(p1->speciesName(i), p2->speciesName(i));
            ASSERT_EQ(p1->molecularWeight(i), p2->molecularWeight(i));
        }
    }
};

TEST_F(CtiConversionTest, ExplicitConversion) {
    p1 = newPhase("../data/air-no-reactions.xml");
    ctml::ct2ctml("../data/air-no-reactions.cti");
    p2 = newPhase("air-no-reactions.xml", "");
    compare();
}

TEST_F(CtiConversionTest, ImplicitConversion) {
    p1 = newPhase("../data/air-no-reactions.xml");
    p2 = newPhase("../data/air-no-reactions.cti");
    compare();
}

class ChemkinConversionTest : public testing::Test {
public:
    void copyInputFile(const std::string& name) {
        std::string in_name = "../data/" + name;
        std::ifstream source(in_name.c_str(), std::ios::binary);
        std::ofstream dest(name.c_str(), std::ios::binary);
        dest << source.rdbuf();
    }
};

TEST_F(ChemkinConversionTest, ValidConversion) {
    copyInputFile("pdep-test.inp");
    ctml::ck2cti("pdep-test.inp");
    ThermoPhase* p = newPhase("pdep-test.cti");
    ASSERT_GT(p->temperature(), 0.0);
    delete p;
}

TEST_F(ChemkinConversionTest, MissingInputFile) {
    ASSERT_THROW(ctml::ck2cti("nonexistent-file.inp"),
                 CanteraError);
}

TEST_F(ChemkinConversionTest, FailedConversion) {
    copyInputFile("h2o2_missingThermo.inp");
    ASSERT_THROW(ctml::ck2cti("h2o2_missingThermo.inp"),
                 CanteraError);
}
#endif

// 2-region NASA coefficients; Order is significantly different from the
// standard NASA format.
double h2o_coeffs[] = {
    1000.0, -3.029372670E+04, -8.490322080E-01, 4.198640560E+00,
    -2.036434100E-03, 6.520402110E-06, -5.487970620E-09, 1.771978170E-12,
    -3.000429710E+04, 4.966770100E+00,  3.033992490E+00, 2.176918040E-03,
    -1.640725180E-07, -9.704198700E-11, 1.682009920E-14};
double h2o_comp[] = {2.0, 1.0, 0.0};

double h2_coeffs[] = {
    1000.0, -9.17935173E+02, 6.83010238E-01, 2.34433112E+00,
    7.98052075E-03, -1.94781510E-05, 2.01572094E-08, -7.37611761E-12,
    -9.50158922E+02, -3.20502331E+00, 3.33727920E+00, -4.94024731E-05,
    4.99456778E-07, -1.79566394E-10, 2.00255376E-14};
double h2_comp[] = {2.0, 0.0, 0.0};

double o2_coeffs[] = {
    1000.0, -1.063943560E+03, 3.657675730E+00, 3.782456360E+00,
    -2.996734160E-03, 9.847302010E-06, -9.681295090E-09, 3.243728370E-12,
    -1.088457720E+03, 5.453231290E+00, 3.282537840E+00, 1.483087540E-03,
    -7.579666690E-07, 2.094705550E-10, -2.167177940E-14};
double o2_comp[] = {0.0, 2.0, 0.0};

double oh_coeffs[] = {
    1000.0, 3.615080560E+03, -1.039254580E-01, 3.992015430E+00,
    -2.401317520E-03, 4.617938410E-06, -3.881133330E-09, 1.364114700E-12,
    3.858657000E+03, 4.476696100E+00, 3.092887670E+00, 5.484297160E-04,
    1.265052280E-07, -8.794615560E-11, 1.174123760E-14};
double oh_comp[] = {1.0, 1.0, 0.0};

// 2-region Shomate coefficients
double co2_coeffs[] = {
    1200.0, 24.99735, 55.18696, -33.69137, 7.948387, -0.136638, -403.6075, 228.2431,
    58.16639, 2.720074, -0.492289, 0.038844, -6.447293, -425.9186, 263.6125};
double co2_comp[] = {0.0, 2.0, 1.0};

class ConstructFromScratch : public testing::Test
{
public:
    ConstructFromScratch() {
        p.addElement("H");
        p.addElement("O");
        p.addElement("C");
    }
    IdealGasPhase p;
};

TEST_F(ConstructFromScratch, AddElements)
{
    ASSERT_EQ((size_t) 3, p.nElements());
    ASSERT_EQ("H", p.elementName(0));
    ASSERT_EQ((size_t) 1, p.elementIndex("O"));
}

TEST_F(ConstructFromScratch, AddSpeciesNasa)
{
    p.setSpeciesThermo(newSpeciesThermoMgr(NASA));
    SpeciesThermo& sp = p.speciesThermo();

    p.addUniqueSpecies("H2O", h2o_comp);
    sp.install("H2O", 0, NASA, h2o_coeffs, 200.0, 3500.0, 101325.0);
    p.addUniqueSpecies("H2", h2_comp);
    sp.install("H2", 1, NASA, h2_coeffs, 200.0, 3500.0, 101325.0);

    ASSERT_EQ((size_t) 2, p.nSpecies());

    p.addUniqueSpecies("O2", o2_comp);
    sp.install("O2", 2, NASA, o2_coeffs, 200.0, 3500.0, 101325.0);
    p.addUniqueSpecies("OH", oh_comp);
    sp.install("OH", 3, NASA, oh_coeffs, 200.0, 3500.0, 101325.0);

    ASSERT_EQ((size_t) 4, p.nSpecies());
    ASSERT_EQ("H2", p.speciesName(1));
    ASSERT_EQ(2, p.nAtoms(2, 1)); // O in O2
    ASSERT_EQ(2, p.nAtoms(0, 0)); // H in H2O
}

} // namespace Cantera
<|endoftext|>


<commit_before>/******************************************************************************
 *
 *  Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *******************************************************************************/

#include "messagelistview/core/filter.h"
#include "messagelistview/core/messageitem.h"

namespace KMail
{

namespace MessageListView
{

namespace Core
{

Filter::Filter()
  : mStatusMask( 0 )
{
}

bool Filter::match( const MessageItem * item ) const
{
  if ( mStatusMask != 0 )
  {
    if ( ! ( mStatusMask & item->status().toQInt32() ) )
      return false;
  }

  if ( !mSearchString.isEmpty() )
  {
    if ( item->subject().indexOf( mSearchString, 0, Qt::CaseInsensitive ) >= 0 )
      return true;
    if ( item->sender().indexOf( mSearchString, 0, Qt::CaseInsensitive ) >= 0 )
      return true;
    if ( item->receiver().indexOf( mSearchString, 0, Qt::CaseInsensitive ) >= 0 )
      return true;

    return false;
  }

  if ( !mTagId.isEmpty() )
    return item->findTag( mTagId ) != 0;

  return true;
}

bool Filter::isEmpty() const
{
  if ( mStatusMask != 0 )
    return false;

  if ( !mSearchString.isEmpty() )
    return false;

  if ( !mTagId.isEmpty() )
    return false;

  return true;
}

void Filter::clear()
{
  mStatusMask = 0;
  mSearchString.clear();
  mTagId.clear();
}

} // namespace Core

} // namespace MessageListView

} // namespace KMail
<commit_msg>Make it possible again to use quick search while the status filter is active<commit_after>/******************************************************************************
 *
 *  Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *******************************************************************************/

#include "messagelistview/core/filter.h"
#include "messagelistview/core/messageitem.h"

namespace KMail
{

namespace MessageListView
{

namespace Core
{

Filter::Filter()
  : mStatusMask( 0 )
{
}

bool Filter::match( const MessageItem * item ) const
{
  if ( mStatusMask != 0 )
  {
    if ( ! ( mStatusMask & item->status().toQInt32() ) )
      return false;
  }

  if ( !mSearchString.isEmpty() )
  {
    bool searchMatches = false;
    if ( item->subject().indexOf( mSearchString, 0, Qt::CaseInsensitive ) >= 0 )
      searchMatches = true;
    if ( item->sender().indexOf( mSearchString, 0, Qt::CaseInsensitive ) >= 0 )
      searchMatches = true;
    if ( item->receiver().indexOf( mSearchString, 0, Qt::CaseInsensitive ) >= 0 )
      searchMatches = true;
    if ( !searchMatches )
      return false;
  }

  if ( !mTagId.isEmpty() ) {
    const bool tagMatches = item->findTag( mTagId ) != 0;
    if ( !tagMatches )
      return false;
  }

  return true;
}

bool Filter::isEmpty() const
{
  if ( mStatusMask != 0 )
    return false;

  if ( !mSearchString.isEmpty() )
    return false;

  if ( !mTagId.isEmpty() )
    return false;

  return true;
}

void Filter::clear()
{
  mStatusMask = 0;
  mSearchString.clear();
  mTagId.clear();
}

} // namespace Core

} // namespace MessageListView

} // namespace KMail
<|endoftext|>


<commit_before>///
/// @file   int128.hpp
/// @brief  Additional integer types used in primecount:
///         int128_t, uint128_t, intfast64_t, intfast128_t, maxint_t,
///         maxuint_t.
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef INTTYPES_HPP
#define INTTYPES_HPP

#include <limits>
#include <stdint.h>

#if __cplusplus >= 201103L
  #include <type_traits>
#endif

#if defined(HAVE_INT128_T)

namespace primecount {

typedef int128_t maxint_t;
typedef uint128_t maxuint_t;

}

#elif defined(HAVE___INT128_T)

#define HAVE_INT128_T

#include <ostream>
#include <string>

namespace primecount {

typedef __int128_t int128_t;
typedef __uint128_t uint128_t;

typedef __int128_t maxint_t;
typedef __uint128_t maxuint_t;

inline std::ostream& operator<<(std::ostream& stream, uint128_t n)
{
  std::string str;

  while (n > 0)
  {
    str += '0' + n % 10;
    n /= 10;
  }
  if (str.empty())
    str = "0";

  stream << std::string(str.rbegin(), str.rend());
  return stream;
}

inline std::ostream& operator<<(std::ostream& stream, int128_t n)
{
  if (n < 0)
  {
    stream << "-";
    n = -n;
  }
  stream << (uint128_t) n;
  return stream;
}

} // namespace

#else /* int128_t not supported */

namespace primecount {

typedef int64_t maxint_t;
typedef int64_t maxuint_t;

}

#endif /* HAVE_INT128_T */

namespace primecount {

/// Fastest 64-bit integer type for division.
/// On most Intel CPUs before 2015 unsigned 64-bit division is about
/// 10 percent faster than signed division. It is likely that in a few
/// years signed and unsigned division will run equally fast.
///
typedef uint64_t intfast64_t;

#if defined(HAVE_INT128_T)

/// Fastest 128-bit integer type for division.
/// On the author's Intel Core-i7 4770 CPU from 2013 using uint128_t
/// instead of int128_t gives 10 percent better performance.
///
typedef uint128_t intfast128_t;

#endif

/// Portable namespace, includes functions which (unlike the versions
/// form the C++ standard library) work with the int128_t and
/// uint128_t types (2014).
///
namespace prt {

#if __cplusplus >= 201103L
  #define CONSTEXPR constexpr
#else
  #define CONSTEXPR
#endif

template <typename T>
struct numeric_limits
{
  static CONSTEXPR T max()
  {
    return std::numeric_limits<T>::max();
  }
};

#if defined(HAVE_INT128_T)

template <>
struct numeric_limits<int128_t>
{
  static CONSTEXPR int128_t max()
  {
    return ~(((int128_t) 1) << 127);
  }
};

template <>
struct numeric_limits<uint128_t>
{
  static CONSTEXPR uint128_t max()
  {
    return ~((uint128_t) 0);
  }
};

#endif
#undef CONSTEXPR

#if __cplusplus >= 201103L

template <typename T>
struct make_signed
{
#ifndef HAVE_INT128_T
  typedef typename std::make_signed<T>::type type;
#else
  typedef typename std::conditional<std::is_same<T, uint8_t>::value, int8_t,
          typename std::conditional<std::is_same<T, uint16_t>::value, int16_t,
          typename std::conditional<std::is_same<T, uint32_t>::value, int32_t,
          typename std::conditional<std::is_same<T, uint64_t>::value, int64_t,
          typename std::conditional<std::is_same<T, uint128_t>::value, int128_t,
          T>::type>::type>::type>::type>::type type;
#endif
};

template <typename T>
struct is_integral
{
  enum
  {
#ifndef HAVE_INT128_T
    value = std::is_integral<T>::value
#else
    value = std::is_integral<T>::value ||
            std::is_same<T, int128_t>::value ||
            std::is_same<T, uint128_t>::value
#endif
  };
};

template <typename T>
struct is_signed
{
  enum
  {
#ifndef HAVE_INT128_T
    value = std::is_signed<T>::value
#else
    value = std::is_signed<T>::value ||
            std::is_same<T, int128_t>::value
#endif
  };
};

#endif /* __cplusplus >= 201103L */

} // namespace prt
} // namespace primecount

#endif /* INTTYPES_HPP */
<commit_msg>Fix maxuint_t typedef<commit_after>///
/// @file   int128.hpp
/// @brief  Additional integer types used in primecount:
///         int128_t, uint128_t, intfast64_t, intfast128_t, maxint_t,
///         maxuint_t.
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef INTTYPES_HPP
#define INTTYPES_HPP

#include <limits>
#include <stdint.h>

#if __cplusplus >= 201103L
  #include <type_traits>
#endif

#if defined(HAVE_INT128_T)

namespace primecount {

typedef int128_t maxint_t;
typedef uint128_t maxuint_t;

}

#elif defined(HAVE___INT128_T)

#define HAVE_INT128_T

#include <ostream>
#include <string>

namespace primecount {

typedef __int128_t int128_t;
typedef __uint128_t uint128_t;

typedef __int128_t maxint_t;
typedef __uint128_t maxuint_t;

inline std::ostream& operator<<(std::ostream& stream, uint128_t n)
{
  std::string str;

  while (n > 0)
  {
    str += '0' + n % 10;
    n /= 10;
  }
  if (str.empty())
    str = "0";

  stream << std::string(str.rbegin(), str.rend());
  return stream;
}

inline std::ostream& operator<<(std::ostream& stream, int128_t n)
{
  if (n < 0)
  {
    stream << "-";
    n = -n;
  }
  stream << (uint128_t) n;
  return stream;
}

} // namespace

#else /* int128_t not supported */

namespace primecount {

typedef int64_t maxint_t;
typedef uint64_t maxuint_t;

}

#endif /* HAVE_INT128_T */

namespace primecount {

/// Fastest 64-bit integer type for division.
/// On most Intel CPUs before 2015 unsigned 64-bit division is about
/// 10 percent faster than signed division. It is likely that in a few
/// years signed and unsigned division will run equally fast.
///
typedef uint64_t intfast64_t;

#if defined(HAVE_INT128_T)

/// Fastest 128-bit integer type for division.
/// On the author's Intel Core-i7 4770 CPU from 2013 using uint128_t
/// instead of int128_t gives 10 percent better performance.
///
typedef uint128_t intfast128_t;

#endif

/// Portable namespace, includes functions which (unlike the versions
/// form the C++ standard library) work with the int128_t and
/// uint128_t types (2014).
///
namespace prt {

#if __cplusplus >= 201103L
  #define CONSTEXPR constexpr
#else
  #define CONSTEXPR
#endif

template <typename T>
struct numeric_limits
{
  static CONSTEXPR T max()
  {
    return std::numeric_limits<T>::max();
  }
};

#if defined(HAVE_INT128_T)

template <>
struct numeric_limits<int128_t>
{
  static CONSTEXPR int128_t max()
  {
    return ~(((int128_t) 1) << 127);
  }
};

template <>
struct numeric_limits<uint128_t>
{
  static CONSTEXPR uint128_t max()
  {
    return ~((uint128_t) 0);
  }
};

#endif
#undef CONSTEXPR

#if __cplusplus >= 201103L

template <typename T>
struct make_signed
{
#ifndef HAVE_INT128_T
  typedef typename std::make_signed<T>::type type;
#else
  typedef typename std::conditional<std::is_same<T, uint8_t>::value, int8_t,
          typename std::conditional<std::is_same<T, uint16_t>::value, int16_t,
          typename std::conditional<std::is_same<T, uint32_t>::value, int32_t,
          typename std::conditional<std::is_same<T, uint64_t>::value, int64_t,
          typename std::conditional<std::is_same<T, uint128_t>::value, int128_t,
          T>::type>::type>::type>::type>::type type;
#endif
};

template <typename T>
struct is_integral
{
  enum
  {
#ifndef HAVE_INT128_T
    value = std::is_integral<T>::value
#else
    value = std::is_integral<T>::value ||
            std::is_same<T, int128_t>::value ||
            std::is_same<T, uint128_t>::value
#endif
  };
};

template <typename T>
struct is_signed
{
  enum
  {
#ifndef HAVE_INT128_T
    value = std::is_signed<T>::value
#else
    value = std::is_signed<T>::value ||
            std::is_same<T, int128_t>::value
#endif
  };
};

#endif /* __cplusplus >= 201103L */

} // namespace prt
} // namespace primecount

#endif /* INTTYPES_HPP */
<|endoftext|>


<commit_before>/* This file is part of Mapnik (c++ mapping toolkit)
 * Copyright (C) 2005 Artem Pavlenko
 *
 * Mapnik is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//$Id: params.hpp 39 2005-04-10 20:39:53Z pavlenko $

#ifndef PARAMS_HPP
#define PARAMS_HPP

#include <map>
//#include <boost/serialization/serialization.hpp>
//#include <boost/serialization/split_member.hpp>
//#include <boost/serialization/map.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/level.hpp>
#include <boost/serialization/tracking.hpp>
#include <boost/serialization/base_object.hpp>

namespace mapnik
{

    typedef std::pair<const std::string,std::string> parameter;
    typedef std::map<const std::string,std::string> param_map;

    class parameters : public param_map
    {
	friend class boost::serialization::access;

	template <typename Archive>
	void save(Archive & ar, const unsigned int /*version*/) const
	{
	    const size_t size = param_map::size();
	    ar & boost::serialization::make_nvp("count",size);
	    param_map::const_iterator itr;
	    for (itr=param_map::begin();itr!=param_map::end();++itr)
	    {
		ar & boost::serialization::make_nvp("name",itr->first);
		ar & boost::serialization::make_nvp("value",itr->second);
	    }
	}

	template <typename Archive>
	void load(Archive & ar, const unsigned int /*version*/)
	{
	    size_t size;
	    ar & boost::serialization::make_nvp("size",size);
	    for (size_t i=0;i<size;++i)
	    {
		std::string name;
		std::string value;
		ar & boost::serialization::make_nvp("name",name);
		ar & boost::serialization::make_nvp("value",value);
		param_map::insert(make_pair(name,value));
	    }
	}
	BOOST_SERIALIZATION_SPLIT_MEMBER()
    public:
	parameters() {}
	const std::string get(std::string const& key) const
	{
	    param_map::const_iterator itr=find(key);
	    if (itr != end())
	    {
		return itr->second;
	    }
	    return std::string();
	}
    };
}

BOOST_CLASS_IMPLEMENTATION(mapnik::parameter, boost::serialization::object_serializable)
BOOST_CLASS_TRACKING(mapnik::parameter, boost::serialization::track_never)

BOOST_CLASS_IMPLEMENTATION(mapnik::parameters, boost::serialization::object_serializable)
BOOST_CLASS_TRACKING(mapnik::parameters, boost::serialization::track_never)

#endif //PARAMS_HPP
<commit_msg>added missing header for boost_1_32_0 serialization lib<commit_after>/* This file is part of Mapnik (c++ mapping toolkit)
 * Copyright (C) 2005 Artem Pavlenko
 *
 * Mapnik is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//$Id: params.hpp 39 2005-04-10 20:39:53Z pavlenko $

#ifndef PARAMS_HPP
#define PARAMS_HPP

#include <map>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/level.hpp>
#include <boost/serialization/tracking.hpp>
#include <boost/serialization/base_object.hpp>

namespace mapnik
{

    typedef std::pair<const std::string,std::string> parameter;
    typedef std::map<const std::string,std::string> param_map;

    class parameters : public param_map
    {
	friend class boost::serialization::access;

	template <typename Archive>
	void save(Archive & ar, const unsigned int /*version*/) const
	{
	    const size_t size = param_map::size();
	    ar & boost::serialization::make_nvp("count",size);
	    param_map::const_iterator itr;
	    for (itr=param_map::begin();itr!=param_map::end();++itr)
	    {
		ar & boost::serialization::make_nvp("name",itr->first);
		ar & boost::serialization::make_nvp("value",itr->second);
	    }
	}

	template <typename Archive>
	void load(Archive & ar, const unsigned int /*version*/)
	{
	    size_t size;
	    ar & boost::serialization::make_nvp("size",size);
	    for (size_t i=0;i<size;++i)
	    {
		std::string name;
		std::string value;
		ar & boost::serialization::make_nvp("name",name);
		ar & boost::serialization::make_nvp("value",value);
		param_map::insert(make_pair(name,value));
	    }
	}
	BOOST_SERIALIZATION_SPLIT_MEMBER()
    public:
	parameters() {}
	const std::string get(std::string const& key) const
	{
	    param_map::const_iterator itr=find(key);
	    if (itr != end())
	    {
		return itr->second;
	    }
	    return std::string();
	}
    };
}

BOOST_CLASS_IMPLEMENTATION(mapnik::parameter, boost::serialization::object_serializable)
BOOST_CLASS_TRACKING(mapnik::parameter, boost::serialization::track_never)

BOOST_CLASS_IMPLEMENTATION(mapnik::parameters, boost::serialization::object_serializable)
BOOST_CLASS_TRACKING(mapnik::parameters, boost::serialization::track_never)

#endif //PARAMS_HPP
<|endoftext|>


<commit_before>#ifndef NEOVIM_CPP__SOCKET_HPP_
#define NEOVIM_CPP__SOCKET_HPP_

#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/system/system_error.hpp>
#include <cstdlib>
#include <iostream>
#include <string>

namespace nvim {

//TODO Implement asynchronous response handler.
class Socket {
public:
    Socket() :
        socket_(io_service_),
        deadline_(io_service_)
    {
        deadline_.expires_at(boost::posix_time::pos_infin);
        check_deadline();
    }

    void connect(const std::string& host,
               const std::string& service, double timeout_sec);

    size_t read(char *rbuf, size_t capacity, double timeout_sec);
    void write(char *sbuf, size_t size, double timeout_sec);

    private:
    void check_deadline();

    boost::asio::io_service io_service_;
    boost::asio::ip::tcp::socket socket_;
    boost::asio::deadline_timer deadline_;
    boost::asio::streambuf input_buffer_;
};

} //namespace nvim

#endif //NEOVIM_CPP__SOCKET_HPP_
<commit_msg>prepare to support number of connection interface<commit_after>#ifndef NEOVIM_CPP__SOCKET_HPP_
#define NEOVIM_CPP__SOCKET_HPP_

#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/generic/stream_protocol.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/system/system_error.hpp>
#include <cstdlib>
#include <iostream>
#include <string>

namespace nvim {

//TODO Implement asynchronous response handler.
class Socket {
public:
    Socket() :
        socket_(io_service_),
        deadline_(io_service_)
    {
        deadline_.expires_at(boost::posix_time::pos_infin);
        check_deadline();
    }

    void connect_tcp(const std::string& host,
               const std::string& service, double timeout_sec);

    size_t read(char *rbuf, size_t capacity, double timeout_sec);
    void write(char *sbuf, size_t size, double timeout_sec);

private:
    void check_deadline();

    boost::asio::io_service io_service_;
    boost::asio::generic::stream_protocol::socket socket_;
    boost::asio::deadline_timer deadline_;
    boost::asio::streambuf input_buffer_;
};

} //namespace nvim

#endif //NEOVIM_CPP__SOCKET_HPP_
<|endoftext|>


<commit_before>#ifndef LIBTEN_NET_HH
#define LIBTEN_NET_HH

#include "ten/descriptors.hh"
#include "ten/task.hh"
#include <memory>

namespace ten {

class hostname_error : public errorx {
public:
    template <class ...A>
    hostname_error(const char *s, A... args) : errorx(s, std::forward<A>(args)...) {}
};

//! perform address resolution and connect fd, task friendly, all errors by exception
void netdial(int fd, const char *addr, uint16_t port, optional_timeout connect_ms) throw (errno_error, hostname_error, task_interrupted);
//! connect fd using task io scheduling
int netconnect(int fd, const address &addr, optional_timeout ms);
//! task friendly accept
int netaccept(int fd, address &addr, int flags, optional_timeout ms);
//! task friendly recv
ssize_t netrecv(int fd, void *buf, size_t len, int flags, optional_timeout ms);
//! task friendly send
ssize_t netsend(int fd, const void *buf, size_t len, int flags, optional_timeout ms);

//! pure-virtual wrapper around socket_fd
class sockbase {
public:
    socket_fd s;

    sockbase(int domain, int type, int protocol=0) throw (errno_error)
        : s(domain, type | SOCK_NONBLOCK, protocol) {}
    sockbase(int fd=-1) noexcept
        : s(fd) {}

    sockbase(const sockbase &) = delete;
    sockbase &operator =(const sockbase &) = delete;

    sockbase(sockbase &&other) = default;
    sockbase &operator = (sockbase &&other) = default;

    virtual ~sockbase() {}

    void close() { s.close(); }
    bool valid() const { return s.valid(); }

    int fcntl(int cmd) { return s.fcntl(cmd); }
    int fcntl(int cmd, long arg) { return s.fcntl(cmd, arg); }

    void bind(address &addr) throw (errno_error) { s.bind(addr); }
    // use a ridiculous number, kernel will truncate to max
    void listen(int backlog=100000) throw (errno_error) { s.listen(backlog); }
    bool getpeername(address &addr) throw (errno_error) __attribute__((warn_unused_result)) {
        return s.getpeername(addr);
    }
    void getsockname(address &addr) throw (errno_error) {
         s.getsockname(addr);
    }
    template <typename T>
    void getsockopt(int level, int optname, T &optval, socklen_t &optlen) throw (errno_error)
    {
        return s.getsockopt(level, optname, optval, optlen);
    }
    template <typename T>
    void setsockopt(int level, int optname, const T &optval, socklen_t optlen) throw (errno_error)
    {
        return s.setsockopt(level, optname, optval, optlen);
    }
    template <typename T>
    void setsockopt(int level, int optname, const T &optval) throw (errno_error)
    {
        return s.setsockopt(level, optname, optval);
    }

    virtual void dial(const char *addr,
            uint16_t port,
            optional_timeout timeout_ms={})
        throw(errno_error, hostname_error, task_interrupted) = 0;

    virtual int connect(const address &addr,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    virtual int accept(address &addr,
            int flags=0,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    virtual ssize_t recv(void *buf,
            size_t len,
            int flags=0,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    virtual ssize_t send(const void *buf,
            size_t len,
            int flags=0,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    ssize_t recvall(void *buf, size_t len, optional_timeout ms={}) {
        size_t pos = 0;
        ssize_t left = len;
        while (pos != len) {
            ssize_t nr = this->recv(&((char *)buf)[pos], left, 0, ms);
            if (nr > 0) {
                pos += nr;
                left -= nr;
            } else {
                break;
            }
        }
        return pos;
    }
};

//! task friendly socket wrapper
class netsock : public sockbase {
public:
    netsock(int domain, int type, int protocol=0) throw (errno_error)
        : sockbase(domain, type, protocol) {}
    netsock(int fd=-1) noexcept
        : sockbase(fd) {}

    netsock(const netsock &) = delete;
    netsock &operator =(const netsock &) = delete;

    netsock(netsock &&other) = default;
    netsock &operator = (netsock &&other) = default;

    //! dial requires a large 8MB stack size for getaddrinfo; throws on error
    void dial(const char *addr,
            uint16_t port,
            optional_timeout timeout_ms={})
        throw(errno_error, hostname_error, task_interrupted) override;

    int connect(const address &addr,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netconnect(s.fd, addr, timeout_ms);
    }

    int accept(address &addr,
            int flags=0,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netaccept(s.fd, addr, flags, timeout_ms);
    }

    ssize_t recv(void *buf,
            size_t len,
            int flags=0,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netrecv(s.fd, buf, len, flags, timeout_ms);
    }

    ssize_t send(const void *buf,
            size_t len,
            int flags=0,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netsend(s.fd, buf, len, flags, timeout_ms);
    }

};

//! task/proc aware socket server
class netsock_server : public std::enable_shared_from_this<netsock_server> {
protected:
    netsock _sock;
    std::string _protocol_name;
    size_t _stacksize;
    optional_timeout _recv_timeout_ms;
public:
    netsock_server(const std::string &protocol_name_,
            size_t stacksize_=default_stacksize,
            optional_timeout recv_timeout_ms={})
        : _protocol_name(protocol_name_),
        _stacksize(stacksize_),
        _recv_timeout_ms(recv_timeout_ms)
    {
    }

    netsock_server(const netsock_server &) = delete;
    netsock_server &operator=(const netsock_server &) = delete;

    //! listen and accept connections
    void serve(const std::string &ipaddr, uint16_t port, unsigned threads=1) {
        address baddr(ipaddr.c_str(), port);
        serve(baddr, threads);
    }

    //! listen and accept connections
    void serve(address &baddr, unsigned threads=1) {
        netsock s = netsock(baddr.family(), SOCK_STREAM);
        // listening sockets we do want to share across exec
        THROW_ON_NONZERO_ERRNO(s.fcntl(F_SETFD, s.fcntl(F_GETFD) ^ FD_CLOEXEC));
        setup_listen_socket(s);
        s.bind(baddr);
        serve(std::move(s), baddr, threads);
    }

    //! listen and accept connections
    void serve(netsock s, address &baddr, unsigned threads=1) {
        std::swap(_sock, s);
        _sock.getsockname(baddr);
        LOG(INFO) << "listening for " << _protocol_name
            << " on " << baddr << " with " << threads << " threads";;
        _sock.listen();
        //std::shared_ptr<int> shutdown_guard((int *)0x8008135, std::bind(&netsock_server::do_shutdown, this));
        auto self = shared_from_this();
        for (unsigned n=1; n<threads; ++n) {
            procspawn(std::bind(&netsock_server::do_accept_loop, self));
        }
        do_accept_loop();
    }

    int listen_fd() const {
        return _sock.s.fd;
    }

protected:

    virtual void setup_listen_socket(netsock &s) {
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1);
    }

    virtual void on_connection(netsock &s) = 0;

    void do_accept_loop() {
        accept_loop();
    }

    virtual void accept_loop() {
        for (;;) {
            address client_addr;
            int fd;
            while ((fd = _sock.accept(client_addr, 0)) > 0) {
                auto self = shared_from_this();
                taskspawn(std::bind(&netsock_server::client_task, std::move(self), fd), _stacksize);
                taskyield(); // yield to new client task
            }
        }
    }

    void client_task(int fd) {
        netsock s(fd);
        try {
            on_connection(s);
        } catch (std::exception &e) {
            LOG(ERROR) << "unhandled client task error: " << e.what();
        }
    }
};

} // end namespace ten

#endif // LIBTEN_NET_HH
<commit_msg>omit needless returns of void<commit_after>#ifndef LIBTEN_NET_HH
#define LIBTEN_NET_HH

#include "ten/descriptors.hh"
#include "ten/task.hh"
#include <memory>

namespace ten {

class hostname_error : public errorx {
public:
    template <class ...A>
    hostname_error(const char *s, A... args) : errorx(s, std::forward<A>(args)...) {}
};

//! perform address resolution and connect fd, task friendly, all errors by exception
void netdial(int fd, const char *addr, uint16_t port, optional_timeout connect_ms) throw (errno_error, hostname_error, task_interrupted);
//! connect fd using task io scheduling
int netconnect(int fd, const address &addr, optional_timeout ms);
//! task friendly accept
int netaccept(int fd, address &addr, int flags, optional_timeout ms);
//! task friendly recv
ssize_t netrecv(int fd, void *buf, size_t len, int flags, optional_timeout ms);
//! task friendly send
ssize_t netsend(int fd, const void *buf, size_t len, int flags, optional_timeout ms);

//! pure-virtual wrapper around socket_fd
class sockbase {
public:
    socket_fd s;

    sockbase(int domain, int type, int protocol=0) throw (errno_error)
        : s(domain, type | SOCK_NONBLOCK, protocol) {}
    sockbase(int fd=-1) noexcept
        : s(fd) {}

    sockbase(const sockbase &) = delete;
    sockbase &operator =(const sockbase &) = delete;

    sockbase(sockbase &&other) = default;
    sockbase &operator = (sockbase &&other) = default;

    virtual ~sockbase() {}

    void close() { s.close(); }
    bool valid() const { return s.valid(); }

    int fcntl(int cmd) { return s.fcntl(cmd); }
    int fcntl(int cmd, long arg) { return s.fcntl(cmd, arg); }

    void bind(address &addr) throw (errno_error) { s.bind(addr); }
    // use a ridiculous number, kernel will truncate to max
    void listen(int backlog=100000) throw (errno_error) { s.listen(backlog); }
    bool getpeername(address &addr) throw (errno_error) __attribute__((warn_unused_result)) {
        return s.getpeername(addr);
    }
    void getsockname(address &addr) throw (errno_error) {
         s.getsockname(addr);
    }
    template <typename T>
    void getsockopt(int level, int optname, T &optval, socklen_t &optlen) throw (errno_error)
    {
        s.getsockopt(level, optname, optval, optlen);
    }
    template <typename T>
    void setsockopt(int level, int optname, const T &optval, socklen_t optlen) throw (errno_error)
    {
        s.setsockopt(level, optname, optval, optlen);
    }
    template <typename T>
    void setsockopt(int level, int optname, const T &optval) throw (errno_error)
    {
        s.setsockopt(level, optname, optval);
    }

    virtual void dial(const char *addr,
            uint16_t port,
            optional_timeout timeout_ms={})
        throw(errno_error, hostname_error, task_interrupted) = 0;

    virtual int connect(const address &addr,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    virtual int accept(address &addr,
            int flags=0,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    virtual ssize_t recv(void *buf,
            size_t len,
            int flags=0,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    virtual ssize_t send(const void *buf,
            size_t len,
            int flags=0,
            optional_timeout ms = {})
        __attribute__((warn_unused_result)) = 0;

    ssize_t recvall(void *buf, size_t len, optional_timeout ms={}) {
        size_t pos = 0;
        ssize_t left = len;
        while (pos != len) {
            ssize_t nr = this->recv(&((char *)buf)[pos], left, 0, ms);
            if (nr > 0) {
                pos += nr;
                left -= nr;
            } else {
                break;
            }
        }
        return pos;
    }
};

//! task friendly socket wrapper
class netsock : public sockbase {
public:
    netsock(int domain, int type, int protocol=0) throw (errno_error)
        : sockbase(domain, type, protocol) {}
    netsock(int fd=-1) noexcept
        : sockbase(fd) {}

    netsock(const netsock &) = delete;
    netsock &operator =(const netsock &) = delete;

    netsock(netsock &&other) = default;
    netsock &operator = (netsock &&other) = default;

    //! dial requires a large 8MB stack size for getaddrinfo; throws on error
    void dial(const char *addr,
            uint16_t port,
            optional_timeout timeout_ms={})
        throw(errno_error, hostname_error, task_interrupted) override;

    int connect(const address &addr,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netconnect(s.fd, addr, timeout_ms);
    }

    int accept(address &addr,
            int flags=0,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netaccept(s.fd, addr, flags, timeout_ms);
    }

    ssize_t recv(void *buf,
            size_t len,
            int flags=0,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netrecv(s.fd, buf, len, flags, timeout_ms);
    }

    ssize_t send(const void *buf,
            size_t len,
            int flags=0,
            optional_timeout timeout_ms={}) override
        __attribute__((warn_unused_result))
    {
        return netsend(s.fd, buf, len, flags, timeout_ms);
    }

};

//! task/proc aware socket server
class netsock_server : public std::enable_shared_from_this<netsock_server> {
protected:
    netsock _sock;
    std::string _protocol_name;
    size_t _stacksize;
    optional_timeout _recv_timeout_ms;
public:
    netsock_server(const std::string &protocol_name_,
            size_t stacksize_=default_stacksize,
            optional_timeout recv_timeout_ms={})
        : _protocol_name(protocol_name_),
        _stacksize(stacksize_),
        _recv_timeout_ms(recv_timeout_ms)
    {
    }

    netsock_server(const netsock_server &) = delete;
    netsock_server &operator=(const netsock_server &) = delete;

    //! listen and accept connections
    void serve(const std::string &ipaddr, uint16_t port, unsigned threads=1) {
        address baddr(ipaddr.c_str(), port);
        serve(baddr, threads);
    }

    //! listen and accept connections
    void serve(address &baddr, unsigned threads=1) {
        netsock s = netsock(baddr.family(), SOCK_STREAM);
        // listening sockets we do want to share across exec
        THROW_ON_NONZERO_ERRNO(s.fcntl(F_SETFD, s.fcntl(F_GETFD) ^ FD_CLOEXEC));
        setup_listen_socket(s);
        s.bind(baddr);
        serve(std::move(s), baddr, threads);
    }

    //! listen and accept connections
    void serve(netsock s, address &baddr, unsigned threads=1) {
        std::swap(_sock, s);
        _sock.getsockname(baddr);
        LOG(INFO) << "listening for " << _protocol_name
            << " on " << baddr << " with " << threads << " threads";;
        _sock.listen();
        //std::shared_ptr<int> shutdown_guard((int *)0x8008135, std::bind(&netsock_server::do_shutdown, this));
        auto self = shared_from_this();
        for (unsigned n=1; n<threads; ++n) {
            procspawn(std::bind(&netsock_server::do_accept_loop, self));
        }
        do_accept_loop();
    }

    int listen_fd() const {
        return _sock.s.fd;
    }

protected:

    virtual void setup_listen_socket(netsock &s) {
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1);
    }

    virtual void on_connection(netsock &s) = 0;

    void do_accept_loop() {
        accept_loop();
    }

    virtual void accept_loop() {
        for (;;) {
            address client_addr;
            int fd;
            while ((fd = _sock.accept(client_addr, 0)) > 0) {
                auto self = shared_from_this();
                taskspawn(std::bind(&netsock_server::client_task, std::move(self), fd), _stacksize);
                taskyield(); // yield to new client task
            }
        }
    }

    void client_task(int fd) {
        netsock s(fd);
        try {
            on_connection(s);
        } catch (std::exception &e) {
            LOG(ERROR) << "unhandled client task error: " << e.what();
        }
    }
};

} // end namespace ten

#endif // LIBTEN_NET_HH
<|endoftext|>