extern "C" int DLLEXPORT layout2d(
    LayoutRect * layout_rects,
    unsigned int num,
    scalar sheet_x,
    scalar sheet_y,
    scalar cut_size,
    Layout ** res)
{
    Sheet sheet;
    sheet.size[0] = sheet_x;
    sheet.size[1] = sheet_y;
    Parts parts;
    for (unsigned int i = 0; i < num; i++)
    {
        auto rect = &layout_rects[i];
        parts.push_back(Part(rect->size[0], rect->size[1],
                             rect->can_rotate != 0, rect->amount));
    }
    Parts sheets;
    sheets.push_back(Part(sheet.size[0], sheet.size[1]));
    ResultsGenerator generator;
    generator.put_SawThickness(cut_size);
    generator.Begin(parts, sheets);
    Result outer_result;
    int ret = generator.NextResult(outer_result) ? 1 : 0;
    if (ret) {
        *res = _make_raskroy_layout(&outer_result.raskroy,
                                    cut_size,
                                    sheet);
    }
    return ret;
}
extern "C" int DLLEXPORT new_layout2d(
    LayoutRect * layout_rects,
    unsigned int num,
    scalar sheet_x,
    scalar sheet_y,
    scalar cut_size,
    Layout ** res)
{
    Rect sheet;
    sheet.Size[0] = sheet_x;
    sheet.Size[1] = sheet_y;
    Parts parts;
    for (unsigned int i = 0; i < num; i++)
    {
        auto rect = &layout_rects[i];
        Part part(rect->size[0], rect->size[1],
                  rect->can_rotate != 0, rect->amount);
        part.Tag = (int)i;
        parts.push_back(part);
    }

    // merge parts with the same relevant characteristics
    std::map<PartKey, std::list<Part*> > unique_parts_map;
    for (auto i = parts.begin(); i != parts.end(); i++) {
        PartKey part_key;
        part_key.rect = i->rect;
        part_key.can_rotate = i->Rotate;
        part_key.normalize();
        unique_parts_map[part_key].push_back(&*i);
    }
    Parts unique_parts;
    for (auto i = unique_parts_map.begin(); i != unique_parts_map.end(); i++) {
        Part part;
        part.rect = i->first.rect;
        part.Rotate = i->first.can_rotate;
        part.parts = i->second;
        // calculate combined amount
        part.Amount = 0;
        for_each(part.parts.begin(), part.parts.end(),
                 [&part](Part * el) { part.Amount += el->Amount; });

        unique_parts.push_back(part);
    }

    LayoutBuilder layout_builder;
    // initialize amounts vector
    Amounts remains(unique_parts.size());
    // assing amount offsets to parts
    // and amounts to remains
    auto offset = 0;
    std::for_each(unique_parts.begin(),
                  unique_parts.end(),
                  [&offset, &remains](Part & part) {
                      part.AmountOffset = offset++;
                      remains[part.AmountOffset] = part.Amount;
                  });
    // initialize sizes lookups
    Sizes sizes[2];
    for (auto s = 0; s <= 1; s++)
    {
        for (auto pPart = unique_parts.begin(); pPart != unique_parts.end(); pPart++)
            sizes[s].AddPart(*pPart, s);

        // order from big to small
        std::sort(sizes[s].begin(), sizes[s].end(), std::greater_equal<Size>());
        for (auto pSize = sizes[s].begin(); pSize != sizes[s].end(); pSize++)
        {
            std::sort(pSize->other_sizes.begin(), pSize->other_sizes.end(),
                    std::greater_equal<OtherSize>());
            pSize->other_sizes.SetMin();
        }
    }
    scalar min_size[2];
    Layout2d optimizer(sizes, min_size, &remains);
    optimizer.put_SawThickness(cut_size);
    int ret = optimizer.new_optimize(sheet, layout_builder) ? 1 : 0;
    if (ret) {
        unique_ptr<Layout> layout(new Layout);
        layout_builder.simplify();
        layout_builder.check();
        layout_builder.to_layout(*layout);
        *res = layout.release();
        // report back new amounts
        for (size_t i = 0; i < parts.size(); i++) {
            layout_rects[i].amount = parts[i].Amount;
        }
    }
    return ret;
}