<commit_before>#include "xchainer/routines/manipulation.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <sstream>
#include <string>
#include <vector>

#include <nonstd/optional.hpp>

#include "xchainer/array.h"
#include "xchainer/context.h"
#include "xchainer/error.h"
#include "xchainer/graph.h"
#include "xchainer/native/native_backend.h"
#include "xchainer/native/native_device.h"
#include "xchainer/shape.h"
#include "xchainer/strides.h"

#include "xchainer/routines/util.h"

namespace xchainer {

Scalar AsScalar(const Array& a) {
    if (a.GetTotalSize() != 1) {
        throw DimensionError("Cannot convert " + std::to_string(a.GetTotalSize()) + "-dimensional array to scalar");
    }

    // Copy to the native device
    Backend& native_backend = GetDefaultContext().GetBackend(native::NativeBackend::kDefaultName);
    Device& native_device = native_backend.GetDevice(0);
    Array native_copy = a.ToDevice(native_device);

    // Retrieve the value
    native_device.Synchronize();
    return VisitDtype(a.dtype(), [&native_copy](auto pt) -> Scalar {
        using T = typename decltype(pt)::type;
        const void* ptr = native_copy.data().get();
        auto typed_ptr = reinterpret_cast<const T*>(ptr);  // NOLINT: reinterpret_cast
        T value = *typed_ptr;
        return Scalar{value};
    });
}

Array Transpose(const Array& a) {
    Shape out_shape{a.shape().rbegin(), a.shape().rend()};
    Strides out_strides{a.strides().rbegin(), a.strides().rend()};
    Array out = internal::MakeArray(out_shape, out_strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes("transpose", {a}, out, {[](const Array& gout, const std::vector<GraphId>&) { return gout.Transpose(); }});
    return out;
}

Array Reshape(const Array& a, const Shape& shape) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    // If the shape is unchanged, just return a view.
    if (in_shape == shape) {
        return a.MakeView();
    }

    // Check for invalid shape.
    int64_t total_size = in_shape.GetTotalSize();
    if (total_size != shape.GetTotalSize()) {
        throw DimensionError("Cannot reshape array of size " + std::to_string(total_size) + " into shape " + shape.ToString());
    }

    int64_t element_size = GetElementSize(a.dtype());
    Strides strides;
    if (total_size == 0) {
        // Calculate the strides for 0-sized array.
        std::vector<int64_t> rev_strides_vec;
        rev_strides_vec.push_back(element_size);
        for (int8_t i = shape.ndim() - 1; i >= 1; --i) {
            rev_strides_vec.push_back(rev_strides_vec.back() * std::max(int64_t{1}, shape[i]));
        }
        strides = Strides{rev_strides_vec.rbegin(), rev_strides_vec.rend()};
    } else {
        // Calculate the strides for non-0-sized array.

        // reduced_shape and reduced_strides are the shortest shape and strides which can be convertible from input shape and strides
        // without copy.
        std::vector<int64_t> reduced_shape;
        std::vector<int64_t> reduced_strides;
        if (in_shape.ndim() == 0) {
            // Input shape is (). Treat as if it were (1).
            reduced_shape.push_back(int64_t{1});
            reduced_strides.push_back(element_size);
        } else {
            // Add the first pair
            reduced_shape.reserve(in_shape.ndim());
            reduced_strides.reserve(in_shape.ndim());
            reduced_shape.push_back(in_shape[0]);
            reduced_strides.push_back(in_strides[0]);
            // Reduce the remaining
            for (int8_t i = 1; i < in_shape.ndim(); ++i) {
                int64_t dim = in_shape[i];
                int64_t st = in_strides[i];
                assert(dim > 0);
                if (dim == 1 && st == 0) {
                    // If the axis has unit-length with no stride, skip this dimension.
                } else if (dim * st == reduced_strides.back()) {
                    // If the pair is compatible with the previous stride, reduce the pair to it.
                    reduced_shape.back() *= dim;
                    reduced_strides.back() = st;
                } else {
                    // Otherwise, add a new shape and stride.
                    reduced_shape.push_back(dim);
                    reduced_strides.push_back(st);
                }
            }
        }
        assert(reduced_shape.size() == reduced_strides.size());
        assert(!reduced_shape.empty());

        // Construct the strides for no-copy reshape.
        // If it's not possible, can_reshape_without_copy will be false.
        bool can_reshape_without_copy = true;
        std::vector<int64_t> strides_vec;
        if (shape.ndim() > 0) {
            int64_t last_stride = reduced_shape[0] * reduced_strides[0];
            size_t i_dim = 0;
            strides_vec.reserve(shape.ndim());
            for (int64_t dim : shape) {
                if (dim == 0) {
                    strides_vec.push_back(last_stride);
                    continue;
                }
                if (i_dim >= reduced_shape.size() || reduced_shape[i_dim] % dim != 0) {
                    strides_vec.clear();
                    can_reshape_without_copy = false;
                    break;
                }
                reduced_shape[i_dim] /= dim;
                last_stride = reduced_shape[i_dim] * reduced_strides[i_dim];
                strides_vec.push_back(last_stride);
                if (reduced_strides[i_dim] == 1) {
                    ++i_dim;
                }
            }
        }

        if (!can_reshape_without_copy) {
            // Reshape without copy is not possible.
            // TODO(niboshi): Implement it
            throw NotImplementedError("Reshape that requires a copy is not implemented yet.");
        }
        assert(strides_vec.size() == shape.size());

        strides = Strides{strides_vec.begin(), strides_vec.end()};
    }

    Array out = internal::MakeArray(shape, strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes(
            "reshape", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }}, {});

    assert(out.shape() == shape);
    assert(out.strides().size() == shape.size());
    return out;
}

Array Squeeze(const Array& a, const nonstd::optional<std::vector<int8_t>>& axis) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    std::vector<int64_t> out_shape;
    std::vector<int64_t> out_strides;

    if (axis.has_value()) {
        std::vector<int8_t> sorted_axis = internal::GetSortedAxes(*axis, in_shape.ndim());

        int64_t i_axis = 0;
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (i_axis < static_cast<int64_t>(sorted_axis.size()) && sorted_axis[i_axis] == i) {
                ++i_axis;
                if (in_shape[i] != 1) {
                    std::ostringstream os;
                    os << "Cannot squeeze out non-unit-length axes, where shape was " << in_shape.ToString();
                    os << " and axes were (";
                    for (auto it = axis->begin(); it != axis->end(); ++it) {
                        if (it != axis->begin()) {
                            os << ", ";
                        }
                        os << *it;
                    }
                    os << (axis->size() == 1 ? ",)." : ").");
                    throw DimensionError(os.str());
                }
            } else {
                out_shape.push_back(in_shape[i]);
                out_strides.push_back(in_strides[i]);
            }
        }
    } else {  // All axes are candidates for removal if none are given.
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] != 1) {
                out_shape.push_back(in_shape[i]);
                out_strides.push_back(in_strides[i]);
            }
        }
    }

    Array out = in_shape.size() == out_shape.size() ? a
                                                    : internal::MakeArray(
                                                              Shape{out_shape.begin(), out_shape.end()},
                                                              Strides{out_strides.begin(), out_strides.end()},
                                                              a.dtype(),
                                                              a.device(),
                                                              a.data(),
                                                              a.offset());
    internal::SetUpOpNodes(
            "squeeze", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }});

    return out;
}

Array BroadcastTo(const Array& array, const Shape& shape) {
    const Shape& in_shape = array.shape();
    const Strides& in_strides = array.strides();

    if (in_shape.size() > shape.size()) {
        throw DimensionError("Cannot broadcast to smaller dimensions");
    }

    std::vector<int64_t> rev_strides;
    rev_strides.reserve(shape.size());
    int8_t i_in = in_shape.ndim() - 1;
    for (int8_t i_out = shape.ndim() - 1; i_out >= 0; --i_out) {
        int64_t out_dim = shape[i_out];
        // If this dimension is to be broadcasted, nonbroadcast_stride is unset.
        // Otherwise, it holds the new stride.
        nonstd::optional<int64_t> nonbroadcast_stride{};
        if (i_in >= 0) {
            int64_t in_dim = in_shape[i_in];
            int64_t in_stride = in_strides[i_in];
            --i_in;
            if (in_dim == 1) {
                // do nothing; broadcast
            } else if (in_dim == out_dim) {
                nonbroadcast_stride = in_stride;
            } else {
                throw DimensionError("Invalid broadcast from " + in_shape.ToString() + " to " + shape.ToString());
            }
        } else {
            // do nothing; broadcast
        }

        if (nonbroadcast_stride.has_value()) {
            // non-broadcast dimension
            rev_strides.push_back(*nonbroadcast_stride);
        } else {
            // broadcast dimension
            rev_strides.push_back(int64_t{0});
        }
    }
    assert(rev_strides.size() == shape.size());
    Array out = internal::MakeArray(
            shape, {rev_strides.rbegin(), rev_strides.rend()}, array.dtype(), array.device(), array.data(), array.offset());

    auto backward_function = [in_shape](const Array& gout, const std::vector<GraphId>&) {
        if (gout.shape() == in_shape) {
            return gout;
        }

        int8_t lead = gout.ndim() - in_shape.ndim();
        std::vector<int8_t> lead_axis(lead);
        std::iota(lead_axis.begin(), lead_axis.end(), int8_t{0});

        std::vector<int8_t> axis{lead_axis};
        for (int8_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] == 1) {
                axis.emplace_back(i + lead);
            }
        }
        axis.erase(std::unique(axis.begin(), axis.end()), axis.end());  // Sum does not accept axis with duplicate elements

        Array gin = gout.Sum(axis, true);
        if (lead > 0) {
            return gin.Squeeze(lead_axis);
        }
        return gin;
    };
    internal::SetUpOpNodes("broadcast_to", {array}, out, {backward_function});

    return out;
}

}  // namespace xchainer
<commit_msg>Add offset<commit_after>#include "xchainer/routines/manipulation.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <sstream>
#include <string>
#include <vector>

#include <nonstd/optional.hpp>

#include "xchainer/array.h"
#include "xchainer/context.h"
#include "xchainer/error.h"
#include "xchainer/graph.h"
#include "xchainer/native/native_backend.h"
#include "xchainer/native/native_device.h"
#include "xchainer/shape.h"
#include "xchainer/strides.h"

#include "xchainer/routines/util.h"

namespace xchainer {

Scalar AsScalar(const Array& a) {
    if (a.GetTotalSize() != 1) {
        throw DimensionError("Cannot convert " + std::to_string(a.GetTotalSize()) + "-dimensional array to scalar");
    }

    // Copy to the native device
    Backend& native_backend = GetDefaultContext().GetBackend(native::NativeBackend::kDefaultName);
    Device& native_device = native_backend.GetDevice(0);
    Array native_copy = a.ToDevice(native_device);

    // Retrieve the value
    native_device.Synchronize();
    return VisitDtype(a.dtype(), [&native_copy](auto pt) -> Scalar {
        using T = typename decltype(pt)::type;
        const void* ptr = static_cast<const uint8_t*>(native_copy.data().get()) + native_copy.offset();
        auto typed_ptr = reinterpret_cast<const T*>(ptr);  // NOLINT: reinterpret_cast
        T value = *typed_ptr;
        return Scalar{value};
    });
}

Array Transpose(const Array& a) {
    Shape out_shape{a.shape().rbegin(), a.shape().rend()};
    Strides out_strides{a.strides().rbegin(), a.strides().rend()};
    Array out = internal::MakeArray(out_shape, out_strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes("transpose", {a}, out, {[](const Array& gout, const std::vector<GraphId>&) { return gout.Transpose(); }});
    return out;
}

Array Reshape(const Array& a, const Shape& shape) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    // If the shape is unchanged, just return a view.
    if (in_shape == shape) {
        return a.MakeView();
    }

    // Check for invalid shape.
    int64_t total_size = in_shape.GetTotalSize();
    if (total_size != shape.GetTotalSize()) {
        throw DimensionError("Cannot reshape array of size " + std::to_string(total_size) + " into shape " + shape.ToString());
    }

    int64_t element_size = GetElementSize(a.dtype());
    Strides strides;
    if (total_size == 0) {
        // Calculate the strides for 0-sized array.
        std::vector<int64_t> rev_strides_vec;
        rev_strides_vec.push_back(element_size);
        for (int8_t i = shape.ndim() - 1; i >= 1; --i) {
            rev_strides_vec.push_back(rev_strides_vec.back() * std::max(int64_t{1}, shape[i]));
        }
        strides = Strides{rev_strides_vec.rbegin(), rev_strides_vec.rend()};
    } else {
        // Calculate the strides for non-0-sized array.

        // reduced_shape and reduced_strides are the shortest shape and strides which can be convertible from input shape and strides
        // without copy.
        std::vector<int64_t> reduced_shape;
        std::vector<int64_t> reduced_strides;
        if (in_shape.ndim() == 0) {
            // Input shape is (). Treat as if it were (1).
            reduced_shape.push_back(int64_t{1});
            reduced_strides.push_back(element_size);
        } else {
            // Add the first pair
            reduced_shape.reserve(in_shape.ndim());
            reduced_strides.reserve(in_shape.ndim());
            reduced_shape.push_back(in_shape[0]);
            reduced_strides.push_back(in_strides[0]);
            // Reduce the remaining
            for (int8_t i = 1; i < in_shape.ndim(); ++i) {
                int64_t dim = in_shape[i];
                int64_t st = in_strides[i];
                assert(dim > 0);
                if (dim == 1 && st == 0) {
                    // If the axis has unit-length with no stride, skip this dimension.
                } else if (dim * st == reduced_strides.back()) {
                    // If the pair is compatible with the previous stride, reduce the pair to it.
                    reduced_shape.back() *= dim;
                    reduced_strides.back() = st;
                } else {
                    // Otherwise, add a new shape and stride.
                    reduced_shape.push_back(dim);
                    reduced_strides.push_back(st);
                }
            }
        }
        assert(reduced_shape.size() == reduced_strides.size());
        assert(!reduced_shape.empty());

        // Construct the strides for no-copy reshape.
        // If it's not possible, can_reshape_without_copy will be false.
        bool can_reshape_without_copy = true;
        std::vector<int64_t> strides_vec;
        if (shape.ndim() > 0) {
            int64_t last_stride = reduced_shape[0] * reduced_strides[0];
            size_t i_dim = 0;
            strides_vec.reserve(shape.ndim());
            for (int64_t dim : shape) {
                if (dim == 0) {
                    strides_vec.push_back(last_stride);
                    continue;
                }
                if (i_dim >= reduced_shape.size() || reduced_shape[i_dim] % dim != 0) {
                    strides_vec.clear();
                    can_reshape_without_copy = false;
                    break;
                }
                reduced_shape[i_dim] /= dim;
                last_stride = reduced_shape[i_dim] * reduced_strides[i_dim];
                strides_vec.push_back(last_stride);
                if (reduced_strides[i_dim] == 1) {
                    ++i_dim;
                }
            }
        }

        if (!can_reshape_without_copy) {
            // Reshape without copy is not possible.
            // TODO(niboshi): Implement it
            throw NotImplementedError("Reshape that requires a copy is not implemented yet.");
        }
        assert(strides_vec.size() == shape.size());

        strides = Strides{strides_vec.begin(), strides_vec.end()};
    }

    Array out = internal::MakeArray(shape, strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes(
            "reshape", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }}, {});

    assert(out.shape() == shape);
    assert(out.strides().size() == shape.size());
    return out;
}

Array Squeeze(const Array& a, const nonstd::optional<std::vector<int8_t>>& axis) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    std::vector<int64_t> out_shape;
    std::vector<int64_t> out_strides;

    if (axis.has_value()) {
        std::vector<int8_t> sorted_axis = internal::GetSortedAxes(*axis, in_shape.ndim());

        int64_t i_axis = 0;
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (i_axis < static_cast<int64_t>(sorted_axis.size()) && sorted_axis[i_axis] == i) {
                ++i_axis;
                if (in_shape[i] != 1) {
                    std::ostringstream os;
                    os << "Cannot squeeze out non-unit-length axes, where shape was " << in_shape.ToString();
                    os << " and axes were (";
                    for (auto it = axis->begin(); it != axis->end(); ++it) {
                        if (it != axis->begin()) {
                            os << ", ";
                        }
                        os << *it;
                    }
                    os << (axis->size() == 1 ? ",)." : ").");
                    throw DimensionError(os.str());
                }
            } else {
                out_shape.push_back(in_shape[i]);
                out_strides.push_back(in_strides[i]);
            }
        }
    } else {  // All axes are candidates for removal if none are given.
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] != 1) {
                out_shape.push_back(in_shape[i]);
                out_strides.push_back(in_strides[i]);
            }
        }
    }

    Array out = in_shape.size() == out_shape.size() ? a
                                                    : internal::MakeArray(
                                                              Shape{out_shape.begin(), out_shape.end()},
                                                              Strides{out_strides.begin(), out_strides.end()},
                                                              a.dtype(),
                                                              a.device(),
                                                              a.data(),
                                                              a.offset());
    internal::SetUpOpNodes(
            "squeeze", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }});

    return out;
}

Array BroadcastTo(const Array& array, const Shape& shape) {
    const Shape& in_shape = array.shape();
    const Strides& in_strides = array.strides();

    if (in_shape.size() > shape.size()) {
        throw DimensionError("Cannot broadcast to smaller dimensions");
    }

    std::vector<int64_t> rev_strides;
    rev_strides.reserve(shape.size());
    int8_t i_in = in_shape.ndim() - 1;
    for (int8_t i_out = shape.ndim() - 1; i_out >= 0; --i_out) {
        int64_t out_dim = shape[i_out];
        // If this dimension is to be broadcasted, nonbroadcast_stride is unset.
        // Otherwise, it holds the new stride.
        nonstd::optional<int64_t> nonbroadcast_stride{};
        if (i_in >= 0) {
            int64_t in_dim = in_shape[i_in];
            int64_t in_stride = in_strides[i_in];
            --i_in;
            if (in_dim == 1) {
                // do nothing; broadcast
            } else if (in_dim == out_dim) {
                nonbroadcast_stride = in_stride;
            } else {
                throw DimensionError("Invalid broadcast from " + in_shape.ToString() + " to " + shape.ToString());
            }
        } else {
            // do nothing; broadcast
        }

        if (nonbroadcast_stride.has_value()) {
            // non-broadcast dimension
            rev_strides.push_back(*nonbroadcast_stride);
        } else {
            // broadcast dimension
            rev_strides.push_back(int64_t{0});
        }
    }
    assert(rev_strides.size() == shape.size());
    Array out = internal::MakeArray(
            shape, {rev_strides.rbegin(), rev_strides.rend()}, array.dtype(), array.device(), array.data(), array.offset());

    auto backward_function = [in_shape](const Array& gout, const std::vector<GraphId>&) {
        if (gout.shape() == in_shape) {
            return gout;
        }

        int8_t lead = gout.ndim() - in_shape.ndim();
        std::vector<int8_t> lead_axis(lead);
        std::iota(lead_axis.begin(), lead_axis.end(), int8_t{0});

        std::vector<int8_t> axis{lead_axis};
        for (int8_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] == 1) {
                axis.emplace_back(i + lead);
            }
        }
        axis.erase(std::unique(axis.begin(), axis.end()), axis.end());  // Sum does not accept axis with duplicate elements

        Array gin = gout.Sum(axis, true);
        if (lead > 0) {
            return gin.Squeeze(lead_axis);
        }
        return gin;
    };
    internal::SetUpOpNodes("broadcast_to", {array}, out, {backward_function});

    return out;
}

}  // namespace xchainer
<|endoftext|>


<commit_before>#include "xchainer/routines/manipulation.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>

#include <nonstd/optional.hpp>

#include "xchainer/array.h"
#include "xchainer/axes.h"
#include "xchainer/device.h"
#include "xchainer/error.h"
#include "xchainer/graph.h"
#include "xchainer/shape.h"
#include "xchainer/strides.h"

namespace xchainer {

Scalar AsScalar(const Array& a) {
    if (a.GetTotalSize() != 1) {
        throw DimensionError{"Cannot convert ", a.GetTotalSize(), "-dimensional array to scalar"};
    }

    // Copy to the native device
    Array native_copy = a.ToNative();

    // Retrieve the value
    return VisitDtype(a.dtype(), [&native_copy](auto pt) -> Scalar {
        using T = typename decltype(pt)::type;
        const uint8_t* ptr = static_cast<const uint8_t*>(native_copy.data().get()) + native_copy.offset();
        auto typed_ptr = reinterpret_cast<const T*>(ptr);  // NOLINT: reinterpret_cast
        return Scalar{*typed_ptr};
    });
}

Array Transpose(const Array& a) {
    Shape out_shape{a.shape().rbegin(), a.shape().rend()};
    Strides out_strides{a.strides().rbegin(), a.strides().rend()};
    Array out = internal::MakeArray(out_shape, out_strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes("transpose", {a}, out, {[](const Array& gout, const std::vector<GraphId>&) { return gout.Transpose(); }});
    return out;
}

Array Reshape(const Array& a, const Shape& newshape) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    // If the shape is unchanged, just return a view.
    if (in_shape == newshape) {
        return a.MakeView();
    }

    // Check for invalid shape.
    int64_t total_size = in_shape.GetTotalSize();
    if (total_size != newshape.GetTotalSize()) {
        throw DimensionError{"Cannot reshape array of size ", total_size, " into shape ", newshape};
    }

    int64_t element_size = GetElementSize(a.dtype());
    Strides strides{};
    if (total_size == 0) {
        // Calculate the strides for 0-sized array.
        strides.resize(newshape.ndim());
        strides.back() = element_size;
        for (int8_t i = newshape.ndim() - 1; i >= 1; --i) {
            strides[i - 1] = strides[i] * std::max(int64_t{1}, newshape[i]);
        }
    } else {
        // Calculate the strides for non-0-sized array.

        // reduced_shape and reduced_strides are the shortest shape and strides which can be convertible from input shape and strides
        // without copy.
        Shape reduced_shape{};
        Strides reduced_strides{};
        if (in_shape.ndim() == 0) {
            // Input shape is (). Treat as if it were (1).
            reduced_shape.push_back(int64_t{1});
            reduced_strides.push_back(element_size);
        } else {
            // Add the first pair
            reduced_shape.emplace_back(in_shape[0]);
            reduced_strides.emplace_back(in_strides[0]);
            // Reduce the remaining
            for (int8_t i = 1; i < in_shape.ndim(); ++i) {
                int64_t dim = in_shape[i];
                int64_t st = in_strides[i];
                assert(dim > 0);
                if (dim == 1 && st == 0) {
                    // If the axis has unit-length with no stride, skip this dimension.
                } else if (dim * st == reduced_strides.back()) {
                    // If the pair is compatible with the previous stride, reduce the pair to it.
                    reduced_shape.back() *= dim;
                    reduced_strides.back() = st;
                } else {
                    // Otherwise, add a new shape and stride.
                    reduced_shape.push_back(dim);
                    reduced_strides.push_back(st);
                }
            }
        }
        assert(reduced_shape.size() == reduced_strides.size());
        assert(!reduced_shape.empty());

        // Construct the strides for no-copy reshape.
        // If it's not possible, can_reshape_without_copy will be false.
        bool can_reshape_without_copy = true;
        if (newshape.ndim() > 0) {
            int64_t last_stride = reduced_shape[0] * reduced_strides[0];
            size_t i_dim = 0;
            for (int64_t dim : newshape) {
                if (dim <= 1) {
                    strides.push_back(last_stride);
                    continue;
                }
                if (i_dim >= reduced_shape.size() || reduced_shape[i_dim] % dim != 0) {
                    strides.clear();
                    can_reshape_without_copy = false;
                    break;
                }
                reduced_shape[i_dim] /= dim;
                last_stride = reduced_shape[i_dim] * reduced_strides[i_dim];
                strides.push_back(last_stride);
                if (reduced_shape[i_dim] == 1) {
                    ++i_dim;
                }
            }
        }

        if (!can_reshape_without_copy) {
            // Reshape without copy is not possible.
            // TODO(niboshi): Implement it
            throw NotImplementedError{"Reshape that requires a copy is not implemented yet."};
        }
        assert(strides.size() == newshape.size());
    }

    Array out = internal::MakeArray(newshape, strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes(
            "reshape", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }}, {});

    assert(out.shape() == newshape);
    assert(out.strides().size() == newshape.size());
    return out;
}

Array Squeeze(const Array& a, const OptionalAxes& axis) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    Shape out_shape{};
    Strides out_strides{};

    if (axis.has_value()) {
        const Axes sorted_axis = internal::GetSortedAxes(*axis, in_shape.ndim());

        int64_t i_axis = 0;
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (i_axis < static_cast<int64_t>(sorted_axis.size()) && sorted_axis[i_axis] == i) {
                ++i_axis;
                if (in_shape[i] != 1) {
                    std::ostringstream os;
                    os << "Cannot squeeze out non-unit-length axes, where shape was " << in_shape.ToString();
                    os << " and axes were (";
                    for (auto it = axis->begin(); it != axis->end(); ++it) {
                        if (it != axis->begin()) {
                            os << ", ";
                        }
                        os << *it;
                    }
                    os << (axis->size() == 1 ? ",)." : ").");
                    throw DimensionError{os.str()};
                }
            } else {
                out_shape.emplace_back(in_shape[i]);
                out_strides.emplace_back(in_strides[i]);
            }
        }
    } else {  // All axes are candidates for removal if none are given.
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] != 1) {
                out_shape.emplace_back(in_shape[i]);
                out_strides.emplace_back(in_strides[i]);
            }
        }
    }

    Array out = in_shape.size() == out_shape.size()
                        ? a
                        : internal::MakeArray(out_shape, out_strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes(
            "squeeze", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }});

    return out;
}

Array BroadcastTo(const Array& array, const Shape& shape) {
    const Shape& in_shape = array.shape();
    const Strides& in_strides = array.strides();

    if (in_shape.size() > shape.size()) {
        throw DimensionError{"Cannot broadcast to smaller dimensions"};
    }

    Strides strides;
    strides.resize(shape.ndim());
    int8_t i_in = in_shape.ndim() - 1;
    for (int8_t i_out = shape.ndim() - 1; i_out >= 0; --i_out) {
        int64_t out_dim = shape[i_out];
        // If this dimension is to be broadcasted, nonbroadcast_stride is unset.
        // Otherwise, it holds the new stride.
        nonstd::optional<int64_t> nonbroadcast_stride{};
        if (i_in >= 0) {
            int64_t in_dim = in_shape[i_in];
            int64_t in_stride = in_strides[i_in];
            --i_in;
            if (in_dim == 1) {
                // do nothing; broadcast
            } else if (in_dim == out_dim) {
                nonbroadcast_stride = in_stride;
            } else {
                throw DimensionError{"Invalid broadcast from ", in_shape, " to ", shape};
            }
        } else {
            // do nothing; broadcast
        }

        if (nonbroadcast_stride.has_value()) {
            // non-broadcast dimension
            strides[i_out] = nonbroadcast_stride.value();
        } else {
            // broadcast dimension
            strides[i_out] = int64_t{0};
        }
    }
    assert(strides.size() == shape.size());
    Array out = internal::MakeArray(shape, strides, array.dtype(), array.device(), array.data(), array.offset());

    auto backward_function = [in_shape](const Array& gout, const std::vector<GraphId>&) {
        if (gout.shape() == in_shape) {
            return gout;
        }

        int8_t lead = gout.ndim() - in_shape.ndim();
        Axes lead_axis{};
        lead_axis.resize(lead);
        std::iota(lead_axis.begin(), lead_axis.end(), int8_t{0});

        Axes axis{lead_axis};
        for (int8_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] == 1) {
                axis.emplace_back(i + lead);
            }
        }
        axis.erase(std::unique(axis.begin(), axis.end()), axis.end());  // Sum does not accept axis with duplicate elements

        Array gin = gout.Sum(axis, true);
        if (lead > 0) {
            return gin.Squeeze(lead_axis);
        }
        return gin;
    };
    internal::SetUpOpNodes("broadcast_to", {array}, out, {backward_function});

    return out;
}

}  // namespace xchainer
<commit_msg>Fix error message in AsScalar<commit_after>#include "xchainer/routines/manipulation.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>

#include <nonstd/optional.hpp>

#include "xchainer/array.h"
#include "xchainer/axes.h"
#include "xchainer/device.h"
#include "xchainer/error.h"
#include "xchainer/graph.h"
#include "xchainer/shape.h"
#include "xchainer/strides.h"

namespace xchainer {

Scalar AsScalar(const Array& a) {
    if (a.GetTotalSize() != 1) {
        throw DimensionError{"Cannot convert an array of size ", a.GetTotalSize(), " to a scalar, size must be 1."};
    }

    // Copy to the native device
    Array native_copy = a.ToNative();

    // Retrieve the value
    return VisitDtype(a.dtype(), [&native_copy](auto pt) -> Scalar {
        using T = typename decltype(pt)::type;
        const uint8_t* ptr = static_cast<const uint8_t*>(native_copy.data().get()) + native_copy.offset();
        auto typed_ptr = reinterpret_cast<const T*>(ptr);  // NOLINT: reinterpret_cast
        return Scalar{*typed_ptr};
    });
}

Array Transpose(const Array& a) {
    Shape out_shape{a.shape().rbegin(), a.shape().rend()};
    Strides out_strides{a.strides().rbegin(), a.strides().rend()};
    Array out = internal::MakeArray(out_shape, out_strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes("transpose", {a}, out, {[](const Array& gout, const std::vector<GraphId>&) { return gout.Transpose(); }});
    return out;
}

Array Reshape(const Array& a, const Shape& newshape) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    // If the shape is unchanged, just return a view.
    if (in_shape == newshape) {
        return a.MakeView();
    }

    // Check for invalid shape.
    int64_t total_size = in_shape.GetTotalSize();
    if (total_size != newshape.GetTotalSize()) {
        throw DimensionError{"Cannot reshape array of size ", total_size, " into shape ", newshape};
    }

    int64_t element_size = GetElementSize(a.dtype());
    Strides strides{};
    if (total_size == 0) {
        // Calculate the strides for 0-sized array.
        strides.resize(newshape.ndim());
        strides.back() = element_size;
        for (int8_t i = newshape.ndim() - 1; i >= 1; --i) {
            strides[i - 1] = strides[i] * std::max(int64_t{1}, newshape[i]);
        }
    } else {
        // Calculate the strides for non-0-sized array.

        // reduced_shape and reduced_strides are the shortest shape and strides which can be convertible from input shape and strides
        // without copy.
        Shape reduced_shape{};
        Strides reduced_strides{};
        if (in_shape.ndim() == 0) {
            // Input shape is (). Treat as if it were (1).
            reduced_shape.push_back(int64_t{1});
            reduced_strides.push_back(element_size);
        } else {
            // Add the first pair
            reduced_shape.emplace_back(in_shape[0]);
            reduced_strides.emplace_back(in_strides[0]);
            // Reduce the remaining
            for (int8_t i = 1; i < in_shape.ndim(); ++i) {
                int64_t dim = in_shape[i];
                int64_t st = in_strides[i];
                assert(dim > 0);
                if (dim == 1 && st == 0) {
                    // If the axis has unit-length with no stride, skip this dimension.
                } else if (dim * st == reduced_strides.back()) {
                    // If the pair is compatible with the previous stride, reduce the pair to it.
                    reduced_shape.back() *= dim;
                    reduced_strides.back() = st;
                } else {
                    // Otherwise, add a new shape and stride.
                    reduced_shape.push_back(dim);
                    reduced_strides.push_back(st);
                }
            }
        }
        assert(reduced_shape.size() == reduced_strides.size());
        assert(!reduced_shape.empty());

        // Construct the strides for no-copy reshape.
        // If it's not possible, can_reshape_without_copy will be false.
        bool can_reshape_without_copy = true;
        if (newshape.ndim() > 0) {
            int64_t last_stride = reduced_shape[0] * reduced_strides[0];
            size_t i_dim = 0;
            for (int64_t dim : newshape) {
                if (dim <= 1) {
                    strides.push_back(last_stride);
                    continue;
                }
                if (i_dim >= reduced_shape.size() || reduced_shape[i_dim] % dim != 0) {
                    strides.clear();
                    can_reshape_without_copy = false;
                    break;
                }
                reduced_shape[i_dim] /= dim;
                last_stride = reduced_shape[i_dim] * reduced_strides[i_dim];
                strides.push_back(last_stride);
                if (reduced_shape[i_dim] == 1) {
                    ++i_dim;
                }
            }
        }

        if (!can_reshape_without_copy) {
            // Reshape without copy is not possible.
            // TODO(niboshi): Implement it
            throw NotImplementedError{"Reshape that requires a copy is not implemented yet."};
        }
        assert(strides.size() == newshape.size());
    }

    Array out = internal::MakeArray(newshape, strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes(
            "reshape", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }}, {});

    assert(out.shape() == newshape);
    assert(out.strides().size() == newshape.size());
    return out;
}

Array Squeeze(const Array& a, const OptionalAxes& axis) {
    const Shape& in_shape = a.shape();
    const Strides& in_strides = a.strides();

    Shape out_shape{};
    Strides out_strides{};

    if (axis.has_value()) {
        const Axes sorted_axis = internal::GetSortedAxes(*axis, in_shape.ndim());

        int64_t i_axis = 0;
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (i_axis < static_cast<int64_t>(sorted_axis.size()) && sorted_axis[i_axis] == i) {
                ++i_axis;
                if (in_shape[i] != 1) {
                    std::ostringstream os;
                    os << "Cannot squeeze out non-unit-length axes, where shape was " << in_shape.ToString();
                    os << " and axes were (";
                    for (auto it = axis->begin(); it != axis->end(); ++it) {
                        if (it != axis->begin()) {
                            os << ", ";
                        }
                        os << *it;
                    }
                    os << (axis->size() == 1 ? ",)." : ").");
                    throw DimensionError{os.str()};
                }
            } else {
                out_shape.emplace_back(in_shape[i]);
                out_strides.emplace_back(in_strides[i]);
            }
        }
    } else {  // All axes are candidates for removal if none are given.
        for (int64_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] != 1) {
                out_shape.emplace_back(in_shape[i]);
                out_strides.emplace_back(in_strides[i]);
            }
        }
    }

    Array out = in_shape.size() == out_shape.size()
                        ? a
                        : internal::MakeArray(out_shape, out_strides, a.dtype(), a.device(), a.data(), a.offset());
    internal::SetUpOpNodes(
            "squeeze", {a}, out, {[in_shape](const Array& gout, const std::vector<GraphId>&) { return gout.Reshape(in_shape); }});

    return out;
}

Array BroadcastTo(const Array& array, const Shape& shape) {
    const Shape& in_shape = array.shape();
    const Strides& in_strides = array.strides();

    if (in_shape.size() > shape.size()) {
        throw DimensionError{"Cannot broadcast to smaller dimensions"};
    }

    Strides strides;
    strides.resize(shape.ndim());
    int8_t i_in = in_shape.ndim() - 1;
    for (int8_t i_out = shape.ndim() - 1; i_out >= 0; --i_out) {
        int64_t out_dim = shape[i_out];
        // If this dimension is to be broadcasted, nonbroadcast_stride is unset.
        // Otherwise, it holds the new stride.
        nonstd::optional<int64_t> nonbroadcast_stride{};
        if (i_in >= 0) {
            int64_t in_dim = in_shape[i_in];
            int64_t in_stride = in_strides[i_in];
            --i_in;
            if (in_dim == 1) {
                // do nothing; broadcast
            } else if (in_dim == out_dim) {
                nonbroadcast_stride = in_stride;
            } else {
                throw DimensionError{"Invalid broadcast from ", in_shape, " to ", shape};
            }
        } else {
            // do nothing; broadcast
        }

        if (nonbroadcast_stride.has_value()) {
            // non-broadcast dimension
            strides[i_out] = nonbroadcast_stride.value();
        } else {
            // broadcast dimension
            strides[i_out] = int64_t{0};
        }
    }
    assert(strides.size() == shape.size());
    Array out = internal::MakeArray(shape, strides, array.dtype(), array.device(), array.data(), array.offset());

    auto backward_function = [in_shape](const Array& gout, const std::vector<GraphId>&) {
        if (gout.shape() == in_shape) {
            return gout;
        }

        int8_t lead = gout.ndim() - in_shape.ndim();
        Axes lead_axis{};
        lead_axis.resize(lead);
        std::iota(lead_axis.begin(), lead_axis.end(), int8_t{0});

        Axes axis{lead_axis};
        for (int8_t i = 0; i < in_shape.ndim(); ++i) {
            if (in_shape[i] == 1) {
                axis.emplace_back(i + lead);
            }
        }
        axis.erase(std::unique(axis.begin(), axis.end()), axis.end());  // Sum does not accept axis with duplicate elements

        Array gin = gout.Sum(axis, true);
        if (lead > 0) {
            return gin.Squeeze(lead_axis);
        }
        return gin;
    };
    internal::SetUpOpNodes("broadcast_to", {array}, out, {backward_function});

    return out;
}

}  // namespace xchainer
<|endoftext|>


<commit_before>class Solution {
public:
    /**
     * @param A an array
     * @return total of reverse pairs
     */
    long long res=0;
    long long reversePairs(vector<int>& A) {
        // Write your code here

        int n=A.size();
        vector<int> t(n,0);

        helper(A,0,n,t);

        return res;
    }

    void helper(vector<int> &A, int x, int y, vector<int>& t){
        if(y-x>1){
            int m=x+((y-x)>>1);
            helper(A,x,m,t);
            helper(A,m,y,t);

            //merge
            int p=x,i=x,q=m;

            while(p<m || q<y){
                if(q>=y || (p<m && A[p]<=A[q])){
                    t[i++]=A[p++];
                }else{
                    // when use a right element to fill in the t,
                    // the A[q] is smaller than A[x] where x is [p,m)
                    // so in terms of A[q], there is m-p reverse pairs
                    res+=(m-p);
                    t[i++]=A[q++];
                }
            }

            for(int i=x; i<y; i++){
                A[i]=t[i];
            }
        }

    }
};<commit_msg>update<commit_after>class Solution {
public:
    /**
     * @param A an array
     * @return total of reverse pairs
     */
    long long res=0;
    long long reversePairs(vector<int>& A) {
        // Write your code here

        int n=A.size();
        vector<int> t(n,0);

        helper(A,0,n,t);

        return res;
    }

    void helper(vector<int> &A, int x, int y, vector<int>& t){
        if(y-x>1){
            int m=x+((y-x)>>1);
            helper(A,x,m,t);
            helper(A,m,y,t);

            //merge
            int p=x,i=x,q=m;

            while(p<m || q<y){
                if(q>=y || (p<m && A[p]<=A[q])){
                    t[i++]=A[p++];
                }else{
                    // when use a right element to fill in the t,
                    // the A[q] is smaller than all the A[x] where x is [p,m)
                    // so in terms of A[q], there is m-p reverse pairs
                    res+=(m-p);
                    t[i++]=A[q++];
                }
            }

            for(int i=x; i<y; i++){
                A[i]=t[i];
            }
        }

    }
};<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/queryeval/searchiterator.h>

#include <algorithm>

using namespace search::fef;

struct State {
    SimpleTermData          term;
    MatchData::UP           md;
    TermFieldMatchData     *f3;
    TermFieldMatchData     *f5;
    TermFieldMatchData     *f7;
    TermFieldMatchDataArray array;

    State();
    ~State();

    void setArray(TermFieldMatchDataArray value) {
        array = value;
    }
};

State::State() : term(), md(), f3(0), f5(0), f7(0), array() {}
State::~State() {}

void testInvalidId() {
    const TermFieldMatchData empty;
    using search::queryeval::SearchIterator;

    EXPECT_EQUAL(TermFieldMatchData::invalidId(), empty.getDocId());
    EXPECT_TRUE(TermFieldMatchData::invalidId() < (SearchIterator::beginId() + 1 ) ||
               TermFieldMatchData::invalidId() > (search::endDocId - 1));
}

void testSetup(State &state) {
    MatchDataLayout layout;

    state.term.addField(3); // docfreq = 1
    state.term.addField(7); // docfreq = 2
    state.term.addField(5); // docfreq = 3

    typedef search::fef::ITermFieldRangeAdapter FRA;
    typedef search::fef::SimpleTermFieldRangeAdapter SFR;

    // lookup terms
    {
        int i = 1;
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setDocFreq(25 * i++, 100);
        }
    }

    // reserve handles
    {
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setHandle(layout.allocTermField(iter.get().getFieldId()));
        }
    }

    state.md = layout.createMatchData();

    // init match data
    {
        for (FRA iter(state.term); iter.valid(); iter.next()) {
            const ITermFieldData& tfd = iter.get();

            TermFieldHandle handle = tfd.getHandle();
            TermFieldMatchData *data = state.md->resolveTermField(handle);
            switch (tfd.getFieldId()) {
            case 3:
                state.f3 = data;
                break;
            case 5:
                state.f5 = data;
                break;
            case 7:
                state.f7 = data;
                break;
            default:
                EXPECT_TRUE(false);
            }
        }
        EXPECT_EQUAL(3u, state.f3->getFieldId());
        EXPECT_EQUAL(5u, state.f5->getFieldId());
        EXPECT_EQUAL(7u, state.f7->getFieldId());
    }

    // test that we can setup array
    EXPECT_EQUAL(false, state.array.valid());
    state.setArray(TermFieldMatchDataArray().add(state.f3).add(state.f5).add(state.f7));
    EXPECT_EQUAL(true, state.array.valid());
}

void testGenerate(State &state) {
    // verify array
    EXPECT_EQUAL(3u, state.array.size());
    EXPECT_EQUAL(state.f3, state.array[0]);
    EXPECT_EQUAL(state.f5, state.array[1]);
    EXPECT_EQUAL(state.f7, state.array[2]);

    // stale unpacked data
    state.f5->reset(5);
    EXPECT_EQUAL(5u, state.f5->getDocId());
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        state.f5->appendPosition(pos);
        EXPECT_EQUAL(1u, state.f5->getIterator().size());
        EXPECT_EQUAL(10u, state.f5->getIterator().getFieldLength());
    }
    state.f5->reset(6);
    EXPECT_EQUAL(6u, state.f5->getDocId());
    EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
               state.f5->getIterator().getFieldLength());
    EXPECT_EQUAL(0u, state.f5->getIterator().size());

    // fresh unpacked data
    state.f3->reset(10);
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
                   state.f3->getIterator().getFieldLength());
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(10u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(15);
        pos.setElementId(1);
        pos.setElementLen(20);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(1);
        pos.setElementId(2);
        pos.setElementLen(5);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }

    // raw score
    state.f7->setRawScore(10, 5.0);
}

void testAnalyze(State &state) {
    EXPECT_EQUAL(10u, state.f3->getDocId());
    EXPECT_NOT_EQUAL(10u, state.f5->getDocId());
    EXPECT_EQUAL(10u, state.f7->getDocId());

    FieldPositionsIterator it = state.f3->getIterator();
    EXPECT_EQUAL(20u, it.getFieldLength());
    EXPECT_EQUAL(3u, it.size());
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(3u, it.getPosition());
    EXPECT_EQUAL(0u, it.getElementId());
    EXPECT_EQUAL(10u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(15u, it.getPosition());
    EXPECT_EQUAL(1u, it.getElementId());
    EXPECT_EQUAL(20u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(1u, it.getPosition());
    EXPECT_EQUAL(2u, it.getElementId());
    EXPECT_EQUAL(5u, it.getElementLen());
    it.next();
    EXPECT_TRUE(!it.valid());

    EXPECT_EQUAL(0.0, state.f3->getRawScore());
    EXPECT_EQUAL(0.0, state.f5->getRawScore());
    EXPECT_EQUAL(5.0, state.f7->getRawScore());
}

TEST("term field model") {
    State state;
    testSetup(state);
    testGenerate(state);
    testAnalyze(state);
    testInvalidId();
}

TEST("append positions") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    tfmd.reset(7);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos(0x01020304, 0x10203040, 0x11223344, 0x12345678);
    tfmd.appendPosition(pos);
    EXPECT_EQUAL(1u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());
    tfmd.reset(11);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos2(0x21020304, 0x20203040, 0x21223344, 0x22345678);
    tfmd.appendPosition(pos);
    tfmd.appendPosition(pos2);
    EXPECT_EQUAL(2u, tfmd.size());
    EXPECT_EQUAL(42u, tfmd.capacity());
    TermFieldMatchDataPosition pos3(0x31020304, 0x30203040, 0x31223344, 0x32345678);
    tfmd.appendPosition(pos3);
    EXPECT_EQUAL(3u, tfmd.size());
    EXPECT_EQUAL(42u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());

    EXPECT_EQUAL(0x21020304u, tfmd.begin()[1].getElementId());
    EXPECT_EQUAL(0x20203040u, tfmd.begin()[1].getPosition());
    EXPECT_EQUAL(0x21223344, tfmd.begin()[1].getElementWeight());
    EXPECT_EQUAL(0x22345678u, tfmd.begin()[1].getElementLen());

    EXPECT_EQUAL(0x31020304u, tfmd.begin()[2].getElementId());
    EXPECT_EQUAL(0x30203040u, tfmd.begin()[2].getPosition());
    EXPECT_EQUAL(0x31223344, tfmd.begin()[2].getElementWeight());
    EXPECT_EQUAL(0x32345678u, tfmd.begin()[2].getElementLen());
}

TEST("Access subqueries") {
    State state;
    testSetup(state);
    state.f3->reset(10);
    state.f3->setSubqueries(10, 42);
    EXPECT_EQUAL(42ULL, state.f3->getSubqueries());
    state.f3->enableRawScore();
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());

    state.f3->reset(11);
    state.f3->appendPosition(TermFieldMatchDataPosition());
    state.f3->setSubqueries(11, 42);
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());
}

TEST("require that TermFieldMatchData can be tagged as needed or not") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(tfmd.needs_normal_features());
    EXPECT_TRUE(tfmd.needs_interleaved_features());
    tfmd.tagAsNotNeeded();
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(tfmd.isNotNeeded());
    EXPECT_TRUE(!tfmd.needs_normal_features());
    EXPECT_TRUE(!tfmd.needs_interleaved_features());
    tfmd.setNeedNormalFeatures(true);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(tfmd.needs_normal_features());
    EXPECT_TRUE(!tfmd.needs_interleaved_features());
    tfmd.setNeedInterleavedFeatures(true);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(tfmd.needs_normal_features());
    EXPECT_TRUE(tfmd.needs_interleaved_features());
    tfmd.setNeedNormalFeatures(false);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(!tfmd.needs_normal_features());
    EXPECT_TRUE(tfmd.needs_interleaved_features());
    tfmd.setNeedInterleavedFeatures(false);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(tfmd.isNotNeeded());
    EXPECT_TRUE(!tfmd.needs_normal_features());
    EXPECT_TRUE(!tfmd.needs_interleaved_features());
}

TEST("require that MatchData soft_reset retains appropriate state") {
    auto md = MatchData::makeTestInstance(10, 10);
    md->set_termwise_limit(0.5);
    auto *old_term = md->resolveTermField(7);
    old_term->tagAsNotNeeded();
    old_term->populate_fixed()->setElementWeight(21);
    old_term->resetOnlyDocId(42);
    EXPECT_EQUAL(md->get_termwise_limit(), 0.5);
    EXPECT_TRUE(old_term->isNotNeeded());
    EXPECT_EQUAL(old_term->getFieldId(), 7u);
    EXPECT_EQUAL(old_term->getWeight(), 21);
    EXPECT_EQUAL(old_term->getDocId(), 42u);
    md->soft_reset();
    auto *new_term = md->resolveTermField(7);
    EXPECT_EQUAL(new_term, old_term);
    EXPECT_EQUAL(md->get_termwise_limit(), 1.0);
    EXPECT_TRUE(new_term->isNotNeeded());
    EXPECT_EQUAL(new_term->getFieldId(), 7u);
    EXPECT_EQUAL(new_term->getWeight(), 21);
    EXPECT_EQUAL(new_term->getDocId(), TermFieldMatchData::invalidId());
}

TEST("require that compareWithExactness implements a strict weak ordering") {
   TermFieldMatchDataPosition a(0, 1, 100, 1);
   TermFieldMatchDataPosition b(0, 2, 100, 1);
   TermFieldMatchDataPosition c(0, 2, 100, 1);
   TermFieldMatchDataPosition d(0, 3, 100, 3);
   TermFieldMatchDataPosition e(0, 3, 100, 3);
   TermFieldMatchDataPosition f(0, 4, 100, 1);

   d.setMatchExactness(0.75);
   e.setMatchExactness(0.5);

   bool (*cmp)(const TermFieldMatchDataPosition &a,
               const TermFieldMatchDataPosition &b) = TermFieldMatchDataPosition::compareWithExactness;

   EXPECT_EQUAL(true, cmp(a, b));
   EXPECT_EQUAL(false, cmp(b, c));
   EXPECT_EQUAL(true, cmp(c, d));
   EXPECT_EQUAL(true, cmp(d, e));
   EXPECT_EQUAL(true, cmp(e, f));

   EXPECT_EQUAL(false, cmp(b, a));
   EXPECT_EQUAL(false, cmp(c, b));
   EXPECT_EQUAL(false, cmp(d, c));
   EXPECT_EQUAL(false, cmp(e, d));
   EXPECT_EQUAL(false, cmp(f, e));
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>Move array<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/fef/fef.h>
#include <vespa/searchlib/queryeval/searchiterator.h>

#include <algorithm>

using namespace search::fef;

struct State {
    SimpleTermData          term;
    MatchData::UP           md;
    TermFieldMatchData     *f3;
    TermFieldMatchData     *f5;
    TermFieldMatchData     *f7;
    TermFieldMatchDataArray array;

    State();
    ~State();

    void setArray(TermFieldMatchDataArray value) {
        array = std::move(value);
    }
};

State::State() : term(), md(), f3(nullptr), f5(nullptr), f7(nullptr), array() {}
State::~State() = default;

void testInvalidId() {
    const TermFieldMatchData empty;
    using search::queryeval::SearchIterator;

    EXPECT_EQUAL(TermFieldMatchData::invalidId(), empty.getDocId());
    EXPECT_TRUE(TermFieldMatchData::invalidId() < (SearchIterator::beginId() + 1 ) ||
               TermFieldMatchData::invalidId() > (search::endDocId - 1));
}

void testSetup(State &state) {
    MatchDataLayout layout;

    state.term.addField(3); // docfreq = 1
    state.term.addField(7); // docfreq = 2
    state.term.addField(5); // docfreq = 3

    typedef search::fef::ITermFieldRangeAdapter FRA;
    typedef search::fef::SimpleTermFieldRangeAdapter SFR;

    // lookup terms
    {
        int i = 1;
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setDocFreq(25 * i++, 100);
        }
    }

    // reserve handles
    {
        for (SFR iter(state.term); iter.valid(); iter.next()) {
            iter.get().setHandle(layout.allocTermField(iter.get().getFieldId()));
        }
    }

    state.md = layout.createMatchData();

    // init match data
    {
        for (FRA iter(state.term); iter.valid(); iter.next()) {
            const ITermFieldData& tfd = iter.get();

            TermFieldHandle handle = tfd.getHandle();
            TermFieldMatchData *data = state.md->resolveTermField(handle);
            switch (tfd.getFieldId()) {
            case 3:
                state.f3 = data;
                break;
            case 5:
                state.f5 = data;
                break;
            case 7:
                state.f7 = data;
                break;
            default:
                EXPECT_TRUE(false);
            }
        }
        EXPECT_EQUAL(3u, state.f3->getFieldId());
        EXPECT_EQUAL(5u, state.f5->getFieldId());
        EXPECT_EQUAL(7u, state.f7->getFieldId());
    }

    // test that we can setup array
    EXPECT_EQUAL(false, state.array.valid());
    state.setArray(TermFieldMatchDataArray().add(state.f3).add(state.f5).add(state.f7));
    EXPECT_EQUAL(true, state.array.valid());
}

void testGenerate(State &state) {
    // verify array
    EXPECT_EQUAL(3u, state.array.size());
    EXPECT_EQUAL(state.f3, state.array[0]);
    EXPECT_EQUAL(state.f5, state.array[1]);
    EXPECT_EQUAL(state.f7, state.array[2]);

    // stale unpacked data
    state.f5->reset(5);
    EXPECT_EQUAL(5u, state.f5->getDocId());
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        state.f5->appendPosition(pos);
        EXPECT_EQUAL(1u, state.f5->getIterator().size());
        EXPECT_EQUAL(10u, state.f5->getIterator().getFieldLength());
    }
    state.f5->reset(6);
    EXPECT_EQUAL(6u, state.f5->getDocId());
    EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
               state.f5->getIterator().getFieldLength());
    EXPECT_EQUAL(0u, state.f5->getIterator().size());

    // fresh unpacked data
    state.f3->reset(10);
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(3);
        pos.setElementId(0);
        pos.setElementLen(10);
        EXPECT_EQUAL(FieldPositionsIterator::UNKNOWN_LENGTH,
                   state.f3->getIterator().getFieldLength());
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(10u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(15);
        pos.setElementId(1);
        pos.setElementLen(20);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }
    {
        TermFieldMatchDataPosition pos;
        pos.setPosition(1);
        pos.setElementId(2);
        pos.setElementLen(5);
        state.f3->appendPosition(pos);
        EXPECT_EQUAL(20u, state.f3->getIterator().getFieldLength());
    }

    // raw score
    state.f7->setRawScore(10, 5.0);
}

void testAnalyze(State &state) {
    EXPECT_EQUAL(10u, state.f3->getDocId());
    EXPECT_NOT_EQUAL(10u, state.f5->getDocId());
    EXPECT_EQUAL(10u, state.f7->getDocId());

    FieldPositionsIterator it = state.f3->getIterator();
    EXPECT_EQUAL(20u, it.getFieldLength());
    EXPECT_EQUAL(3u, it.size());
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(3u, it.getPosition());
    EXPECT_EQUAL(0u, it.getElementId());
    EXPECT_EQUAL(10u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(15u, it.getPosition());
    EXPECT_EQUAL(1u, it.getElementId());
    EXPECT_EQUAL(20u, it.getElementLen());
    it.next();
    EXPECT_TRUE(it.valid());
    EXPECT_EQUAL(1u, it.getPosition());
    EXPECT_EQUAL(2u, it.getElementId());
    EXPECT_EQUAL(5u, it.getElementLen());
    it.next();
    EXPECT_TRUE(!it.valid());

    EXPECT_EQUAL(0.0, state.f3->getRawScore());
    EXPECT_EQUAL(0.0, state.f5->getRawScore());
    EXPECT_EQUAL(5.0, state.f7->getRawScore());
}

TEST("term field model") {
    State state;
    testSetup(state);
    testGenerate(state);
    testAnalyze(state);
    testInvalidId();
}

TEST("append positions") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    tfmd.reset(7);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos(0x01020304, 0x10203040, 0x11223344, 0x12345678);
    tfmd.appendPosition(pos);
    EXPECT_EQUAL(1u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());
    tfmd.reset(11);
    EXPECT_EQUAL(0u, tfmd.size());
    EXPECT_EQUAL(1u, tfmd.capacity());
    TermFieldMatchDataPosition pos2(0x21020304, 0x20203040, 0x21223344, 0x22345678);
    tfmd.appendPosition(pos);
    tfmd.appendPosition(pos2);
    EXPECT_EQUAL(2u, tfmd.size());
    EXPECT_EQUAL(42u, tfmd.capacity());
    TermFieldMatchDataPosition pos3(0x31020304, 0x30203040, 0x31223344, 0x32345678);
    tfmd.appendPosition(pos3);
    EXPECT_EQUAL(3u, tfmd.size());
    EXPECT_EQUAL(42u, tfmd.capacity());
    EXPECT_EQUAL(0x01020304u, tfmd.begin()->getElementId());
    EXPECT_EQUAL(0x10203040u, tfmd.begin()->getPosition());
    EXPECT_EQUAL(0x11223344, tfmd.begin()->getElementWeight());
    EXPECT_EQUAL(0x12345678u, tfmd.begin()->getElementLen());

    EXPECT_EQUAL(0x21020304u, tfmd.begin()[1].getElementId());
    EXPECT_EQUAL(0x20203040u, tfmd.begin()[1].getPosition());
    EXPECT_EQUAL(0x21223344, tfmd.begin()[1].getElementWeight());
    EXPECT_EQUAL(0x22345678u, tfmd.begin()[1].getElementLen());

    EXPECT_EQUAL(0x31020304u, tfmd.begin()[2].getElementId());
    EXPECT_EQUAL(0x30203040u, tfmd.begin()[2].getPosition());
    EXPECT_EQUAL(0x31223344, tfmd.begin()[2].getElementWeight());
    EXPECT_EQUAL(0x32345678u, tfmd.begin()[2].getElementLen());
}

TEST("Access subqueries") {
    State state;
    testSetup(state);
    state.f3->reset(10);
    state.f3->setSubqueries(10, 42);
    EXPECT_EQUAL(42ULL, state.f3->getSubqueries());
    state.f3->enableRawScore();
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());

    state.f3->reset(11);
    state.f3->appendPosition(TermFieldMatchDataPosition());
    state.f3->setSubqueries(11, 42);
    EXPECT_EQUAL(0ULL, state.f3->getSubqueries());
}

TEST("require that TermFieldMatchData can be tagged as needed or not") {
    TermFieldMatchData tfmd;
    tfmd.setFieldId(123);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(tfmd.needs_normal_features());
    EXPECT_TRUE(tfmd.needs_interleaved_features());
    tfmd.tagAsNotNeeded();
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(tfmd.isNotNeeded());
    EXPECT_TRUE(!tfmd.needs_normal_features());
    EXPECT_TRUE(!tfmd.needs_interleaved_features());
    tfmd.setNeedNormalFeatures(true);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(tfmd.needs_normal_features());
    EXPECT_TRUE(!tfmd.needs_interleaved_features());
    tfmd.setNeedInterleavedFeatures(true);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(tfmd.needs_normal_features());
    EXPECT_TRUE(tfmd.needs_interleaved_features());
    tfmd.setNeedNormalFeatures(false);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(!tfmd.isNotNeeded());
    EXPECT_TRUE(!tfmd.needs_normal_features());
    EXPECT_TRUE(tfmd.needs_interleaved_features());
    tfmd.setNeedInterleavedFeatures(false);
    EXPECT_EQUAL(tfmd.getFieldId(),123u);
    EXPECT_TRUE(tfmd.isNotNeeded());
    EXPECT_TRUE(!tfmd.needs_normal_features());
    EXPECT_TRUE(!tfmd.needs_interleaved_features());
}

TEST("require that MatchData soft_reset retains appropriate state") {
    auto md = MatchData::makeTestInstance(10, 10);
    md->set_termwise_limit(0.5);
    auto *old_term = md->resolveTermField(7);
    old_term->tagAsNotNeeded();
    old_term->populate_fixed()->setElementWeight(21);
    old_term->resetOnlyDocId(42);
    EXPECT_EQUAL(md->get_termwise_limit(), 0.5);
    EXPECT_TRUE(old_term->isNotNeeded());
    EXPECT_EQUAL(old_term->getFieldId(), 7u);
    EXPECT_EQUAL(old_term->getWeight(), 21);
    EXPECT_EQUAL(old_term->getDocId(), 42u);
    md->soft_reset();
    auto *new_term = md->resolveTermField(7);
    EXPECT_EQUAL(new_term, old_term);
    EXPECT_EQUAL(md->get_termwise_limit(), 1.0);
    EXPECT_TRUE(new_term->isNotNeeded());
    EXPECT_EQUAL(new_term->getFieldId(), 7u);
    EXPECT_EQUAL(new_term->getWeight(), 21);
    EXPECT_EQUAL(new_term->getDocId(), TermFieldMatchData::invalidId());
}

TEST("require that compareWithExactness implements a strict weak ordering") {
   TermFieldMatchDataPosition a(0, 1, 100, 1);
   TermFieldMatchDataPosition b(0, 2, 100, 1);
   TermFieldMatchDataPosition c(0, 2, 100, 1);
   TermFieldMatchDataPosition d(0, 3, 100, 3);
   TermFieldMatchDataPosition e(0, 3, 100, 3);
   TermFieldMatchDataPosition f(0, 4, 100, 1);

   d.setMatchExactness(0.75);
   e.setMatchExactness(0.5);

   bool (*cmp)(const TermFieldMatchDataPosition &a,
               const TermFieldMatchDataPosition &b) = TermFieldMatchDataPosition::compareWithExactness;

   EXPECT_EQUAL(true, cmp(a, b));
   EXPECT_EQUAL(false, cmp(b, c));
   EXPECT_EQUAL(true, cmp(c, d));
   EXPECT_EQUAL(true, cmp(d, e));
   EXPECT_EQUAL(true, cmp(e, f));

   EXPECT_EQUAL(false, cmp(b, a));
   EXPECT_EQUAL(false, cmp(c, b));
   EXPECT_EQUAL(false, cmp(d, c));
   EXPECT_EQUAL(false, cmp(e, d));
   EXPECT_EQUAL(false, cmp(f, e));
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"
#include "core/dom/ChildFrameDisconnector.h"

#include "core/dom/shadow/ElementShadow.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "wtf/Assertions.h"

namespace WebCore {

#ifndef NDEBUG
static unsigned checkConnectedSubrameCountIsConsistent(Node&);
#endif

void ChildFrameDisconnector::disconnect(DisconnectPolicy policy)
{
#ifndef NDEBUG
    checkConnectedSubrameCountIsConsistent(m_root);
#endif

    if (!m_root.connectedSubframeCount())
        return;

    if (policy == RootAndDescendants) {
        collectFrameOwners(m_root);
    } else {
        for (Node* child = m_root.firstChild(); child; child = child->nextSibling())
            collectFrameOwners(*child);
    }

    disconnectCollectedFrameOwners();
}

void ChildFrameDisconnector::collectFrameOwners(Node& root)
{
    if (!root.connectedSubframeCount())
        return;

    if (root.isHTMLElement() && root.isFrameOwnerElement())
        m_frameOwners.append(&toHTMLFrameOwnerElement(root));

    for (Node* child = root.firstChild(); child; child = child->nextSibling())
        collectFrameOwners(*child);

    ElementShadow* shadow = root.isElementNode() ? toElement(root).shadow() : 0;
    if (shadow)
        collectFrameOwners(*shadow);
}

void ChildFrameDisconnector::disconnectCollectedFrameOwners()
{
    // Must disable frame loading in the subtree so an unload handler cannot
    // insert more frames and create loaded frames in detached subtrees.
    SubframeLoadingDisabler disabler(m_root);

    for (unsigned i = 0; i < m_frameOwners.size(); ++i) {
        HTMLFrameOwnerElement* owner = m_frameOwners[i].get();
        // Don't need to traverse up the tree for the first owner since no
        // script could have moved it.
        if (!i || m_root.containsIncludingShadowDOM(owner))
            owner->disconnectContentFrame();
    }
}

void ChildFrameDisconnector::collectFrameOwners(ElementShadow& shadow)
{
    for (ShadowRoot* root = shadow.youngestShadowRoot(); root; root = root->olderShadowRoot())
        collectFrameOwners(*root);
}

#ifndef NDEBUG
static unsigned checkConnectedSubrameCountIsConsistent(Node& node)
{
    unsigned count = 0;

    if (node.isElementNode()) {
        if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame())
            count++;

        if (ElementShadow* shadow = toElement(node).shadow()) {
            for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
                count += checkConnectedSubrameCountIsConsistent(*root);
        }
    }

    for (Node* child = node.firstChild(); child; child = child->nextSibling())
        count += checkConnectedSubrameCountIsConsistent(*child);

    // If we undercount there's possibly a security bug since we'd leave frames
    // in subtrees outside the document.
    ASSERT(node.connectedSubframeCount() >= count);

    // If we overcount it's safe, but not optimal because it means we'll traverse
    // through the document in ChildFrameDisconnector looking for frames that have
    // already been disconnected.
    ASSERT(node.connectedSubframeCount() == count);

    return count;
}
#endif

}
<commit_msg>Fix typo checkConnectedSubf(r)ameCountIsConsistent<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"
#include "core/dom/ChildFrameDisconnector.h"

#include "core/dom/shadow/ElementShadow.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "wtf/Assertions.h"

namespace WebCore {

#ifndef NDEBUG
static unsigned checkConnectedSubframeCountIsConsistent(Node&);
#endif

void ChildFrameDisconnector::disconnect(DisconnectPolicy policy)
{
#ifndef NDEBUG
    checkConnectedSubframeCountIsConsistent(m_root);
#endif

    if (!m_root.connectedSubframeCount())
        return;

    if (policy == RootAndDescendants) {
        collectFrameOwners(m_root);
    } else {
        for (Node* child = m_root.firstChild(); child; child = child->nextSibling())
            collectFrameOwners(*child);
    }

    disconnectCollectedFrameOwners();
}

void ChildFrameDisconnector::collectFrameOwners(Node& root)
{
    if (!root.connectedSubframeCount())
        return;

    if (root.isHTMLElement() && root.isFrameOwnerElement())
        m_frameOwners.append(&toHTMLFrameOwnerElement(root));

    for (Node* child = root.firstChild(); child; child = child->nextSibling())
        collectFrameOwners(*child);

    ElementShadow* shadow = root.isElementNode() ? toElement(root).shadow() : 0;
    if (shadow)
        collectFrameOwners(*shadow);
}

void ChildFrameDisconnector::disconnectCollectedFrameOwners()
{
    // Must disable frame loading in the subtree so an unload handler cannot
    // insert more frames and create loaded frames in detached subtrees.
    SubframeLoadingDisabler disabler(m_root);

    for (unsigned i = 0; i < m_frameOwners.size(); ++i) {
        HTMLFrameOwnerElement* owner = m_frameOwners[i].get();
        // Don't need to traverse up the tree for the first owner since no
        // script could have moved it.
        if (!i || m_root.containsIncludingShadowDOM(owner))
            owner->disconnectContentFrame();
    }
}

void ChildFrameDisconnector::collectFrameOwners(ElementShadow& shadow)
{
    for (ShadowRoot* root = shadow.youngestShadowRoot(); root; root = root->olderShadowRoot())
        collectFrameOwners(*root);
}

#ifndef NDEBUG
static unsigned checkConnectedSubframeCountIsConsistent(Node& node)
{
    unsigned count = 0;

    if (node.isElementNode()) {
        if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame())
            count++;

        if (ElementShadow* shadow = toElement(node).shadow()) {
            for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
                count += checkConnectedSubframeCountIsConsistent(*root);
        }
    }

    for (Node* child = node.firstChild(); child; child = child->nextSibling())
        count += checkConnectedSubframeCountIsConsistent(*child);

    // If we undercount there's possibly a security bug since we'd leave frames
    // in subtrees outside the document.
    ASSERT(node.connectedSubframeCount() >= count);

    // If we overcount it's safe, but not optimal because it means we'll traverse
    // through the document in ChildFrameDisconnector looking for frames that have
    // already been disconnected.
    ASSERT(node.connectedSubframeCount() == count);

    return count;
}
#endif

}
<|endoftext|>


<commit_before>#include <QString>
#include <QtTest>
#include <QSignalSpy>
#include <QThread>
#include "../../Kakuro/playstatus.h"

Q_DECLARE_METATYPE(playstatus::Status)
//Q_ENUMS(playstatus::Status)

namespace ps = playstatus;
namespace pd = problemdata;

class PlayStatusTest : public QObject
{
    Q_OBJECT

public:
    PlayStatusTest();

private Q_SLOTS:
    void initTestCase();
    void testStatusChanged();
    void testElapsedTime();
};

PlayStatusTest::PlayStatusTest()
{
}

void PlayStatusTest::initTestCase()
{
}

void PlayStatusTest::testStatusChanged()
{
    ps::PlayStatus target;
    qRegisterMetaType<playstatus::Status>("Status");
    QSignalSpy spy(&target, &ps::PlayStatus::statusChanged);

    QCOMPARE(spy.count(), 0);

    // nothing should happen without data
    target.playPressed();
    QCOMPARE(spy.count(), 0);

    // NODATA -> READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    QCOMPARE(spy.count(), 1);

    // READY -> INPLAY
    target.playPressed();
    QCOMPARE(spy.count(), 2);

    // INPLAY -> PAUSED
    target.playPressed();
    QCOMPARE(spy.count(), 3);

    // PAUSED -> INPLAY
    target.playPressed();
    QCOMPARE(spy.count(), 4);

    // INPLAY -> DONE
    target.solved();
    QCOMPARE(spy.count(), 5);

    ps::Status s;
    s = qvariant_cast<ps::Status>(spy.at(0).at(0));
    QCOMPARE(s, ps::Status::READY);
    s = qvariant_cast<ps::Status>(spy.at(1).at(0));
    QCOMPARE(s, ps::Status::INPLAY);
    s = qvariant_cast<ps::Status>(spy.at(2).at(0));
    QCOMPARE(s, ps::Status::PAUSED);
    s = qvariant_cast<ps::Status>(spy.at(3).at(0));
    QCOMPARE(s, ps::Status::INPLAY);
    s = qvariant_cast<ps::Status>(spy.at(4).at(0));
    QCOMPARE(s, ps::Status::DONE);
}

void PlayStatusTest::testElapsedTime()
{
    static const unsigned long WAIT_MS = 100ul;
    ps::PlayStatus target;

    qint64 s;

    // NODATA
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);

    // READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);

    // INPLAY
    target.playPressed();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QVERIFY(target.getElapsedTime() > s);

    // PAUSED
    target.playPressed();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);

    // INPLAY again
    target.playPressed();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QVERIFY(target.getElapsedTime() > s);

    // DONE
    target.solved();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);
}

QTEST_APPLESS_MAIN(PlayStatusTest)

#include "tst_playstatustest.moc"
<commit_msg>Added test cases for giveup<commit_after>#include <QString>
#include <QtTest>
#include <QSignalSpy>
#include <QThread>
#include "../../Kakuro/playstatus.h"

Q_DECLARE_METATYPE(playstatus::Status)
//Q_ENUMS(playstatus::Status)

namespace ps = playstatus;
namespace pd = problemdata;

class PlayStatusTest : public QObject
{
    Q_OBJECT

public:
    PlayStatusTest();

private Q_SLOTS:
    void initTestCase();
    void testStatusChanged();
    void testGiveupFromNodata();
    void testGiveupFromReady();
    void testGiveupFromInplay();
    void testGiveupFromPaused();
    void testElapsedTime();
    void testElpasedAfterGiveup();
};

PlayStatusTest::PlayStatusTest()
{
}

void PlayStatusTest::initTestCase()
{
}

void PlayStatusTest::testStatusChanged()
{
    ps::PlayStatus target;
    qRegisterMetaType<playstatus::Status>("Status");
    QSignalSpy spy(&target, &ps::PlayStatus::statusChanged);

    QCOMPARE(spy.count(), 0);

    // nothing should happen without data
    target.playPressed();
    QCOMPARE(spy.count(), 0);

    // NODATA -> READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    QCOMPARE(spy.count(), 1);

    // READY -> INPLAY
    target.playPressed();
    QCOMPARE(spy.count(), 2);

    // INPLAY -> PAUSED
    target.playPressed();
    QCOMPARE(spy.count(), 3);

    // PAUSED -> INPLAY
    target.playPressed();
    QCOMPARE(spy.count(), 4);

    // INPLAY -> DONE
    target.solved();
    QCOMPARE(spy.count(), 5);

    ps::Status s;
    s = qvariant_cast<ps::Status>(spy.at(0).at(0));
    QCOMPARE(s, ps::Status::READY);
    s = qvariant_cast<ps::Status>(spy.at(1).at(0));
    QCOMPARE(s, ps::Status::INPLAY);
    s = qvariant_cast<ps::Status>(spy.at(2).at(0));
    QCOMPARE(s, ps::Status::PAUSED);
    s = qvariant_cast<ps::Status>(spy.at(3).at(0));
    QCOMPARE(s, ps::Status::INPLAY);
    s = qvariant_cast<ps::Status>(spy.at(4).at(0));
    QCOMPARE(s, ps::Status::DONE);
}

void PlayStatusTest::testGiveupFromNodata()
{
    ps::PlayStatus target;
    qRegisterMetaType<playstatus::Status>("Status");
    QSignalSpy spy(&target, &ps::PlayStatus::statusChanged);

    // you can't give up without data
    target.giveup();
    QCOMPARE(spy.count(), 0);
}

void PlayStatusTest::testGiveupFromReady()
{
    ps::PlayStatus target;
    qRegisterMetaType<playstatus::Status>("Status");
    QSignalSpy spy(&target, &ps::PlayStatus::statusChanged);

    // NODATA -> READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    // QCOMPARE(spy.count(), 1);

    // you can't give up before start
    target.giveup();
    QCOMPARE(spy.count(), 1);
}

void PlayStatusTest::testGiveupFromInplay()
{
    ps::PlayStatus target;
    qRegisterMetaType<playstatus::Status>("Status");
    QSignalSpy spy(&target, &ps::PlayStatus::statusChanged);

    // NODATA -> READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    // QCOMPARE(spy.count(), 1);

    // READY -> INPLAY
    target.playPressed();
    // QCOMPARE(spy.count(), 2);

    target.giveup();
    QCOMPARE(spy.count(), 3);
    ps::Status s;
    s = qvariant_cast<ps::Status>(spy.at(2).at(0));
    QCOMPARE(s, ps::Status::DONE);
}

void PlayStatusTest::testGiveupFromPaused()
{
    ps::PlayStatus target;
    qRegisterMetaType<playstatus::Status>("Status");
    QSignalSpy spy(&target, &ps::PlayStatus::statusChanged);

    // NODATA -> READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    // QCOMPARE(spy.count(), 1);

    // READY -> INPLAY
    target.playPressed();
    // QCOMPARE(spy.count(), 2);

    // INPLAY -> PAUSED
    target.playPressed();
    // QCOMPARE(spy.count(), 3);

    target.giveup();
    QCOMPARE(spy.count(), 4);
    ps::Status s;
    s = qvariant_cast<ps::Status>(spy.at(3).at(0));
    QCOMPARE(s, ps::Status::DONE);
}

void PlayStatusTest::testElapsedTime()
{
    static const unsigned long WAIT_MS = 100ul;
    ps::PlayStatus target;

    qint64 s;

    // NODATA
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);

    // READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);

    // INPLAY
    target.playPressed();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QVERIFY(target.getElapsedTime() > s);

    // PAUSED
    target.playPressed();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);

    // INPLAY again
    target.playPressed();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QVERIFY(target.getElapsedTime() > s);

    // DONE
    target.solved();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);
}

void PlayStatusTest::testElpasedAfterGiveup()
{
    static const unsigned long WAIT_MS = 100ul;
    ps::PlayStatus target;

    qint64 s;

    // NODATA -> READY
    std::shared_ptr<pd::ProblemData> pMockPd{pd::ProblemData::problemLoader("dummy")};
    target.updateData(pMockPd);

    // READY -> INPLAY
    target.playPressed();

    // give up
    target.giveup();
    s = target.getElapsedTime();
    QThread::msleep(WAIT_MS);
    QCOMPARE(target.getElapsedTime(), s);
}

QTEST_APPLESS_MAIN(PlayStatusTest)

#include "tst_playstatustest.moc"
<|endoftext|>