<commit_before>#include "SlidingWindow.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Scope.h"
#include "Debug.h"
#include "Substitute.h"
#include "IRPrinter.h"
#include "Simplify.h"
#include "Monotonic.h"
#include "Bounds.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;

namespace {

// Does an expression depend on a particular variable?
class ExprDependsOnVar : public IRVisitor {
    using IRVisitor::visit;

    void visit(const Variable *op) {
        if (op->name == var) result = true;
    }

    void visit(const Let *op) {
        op->value.accept(this);
        // The name might be hidden within the body of the let, in
        // which case there's no point descending.
        if (op->name != var) {
            op->body.accept(this);
        }
    }
public:

    bool result;
    string var;

    ExprDependsOnVar(string v) : result(false), var(v) {
    }
};

bool expr_depends_on_var(Expr e, string v) {
    ExprDependsOnVar depends(v);
    e.accept(&depends);
    return depends.result;
}

class ExpandExpr : public IRMutator {
    using IRMutator::visit;
    const Scope<Expr> &scope;

    void visit(const Variable *var) {
        if (scope.contains(var->name)) {
            expr = scope.get(var->name);
            debug(3) << "Fully expanded " << var->name << " -> " << expr << "\n";
        } else {
            expr = var;
        }
    }

public:
    ExpandExpr(const Scope<Expr> &s) : scope(s) {}

};

// Perform all the substitutions in a scope
Expr expand_expr(Expr e, const Scope<Expr> &scope) {
    ExpandExpr ee(scope);
    Expr result = ee.mutate(e);
    debug(3) << "Expanded " << e << " into " << result << "\n";
    return result;
}

}

// Perform sliding window optimization for a function over a
// particular serial for loop
class SlidingWindowOnFunctionAndLoop : public IRMutator {
    Function func;
    string loop_var;
    Expr loop_min;
    Scope<Expr> scope;

    map<string, Expr> replacements;

    using IRMutator::visit;

    // Check if the dimension at index 'dim_idx' is always pure (i.e. equal to 'dim')
    // in the definition (including in its specializations)
    bool is_dim_always_pure(const Definition &def, const string& dim, int dim_idx) {
        const Variable *var = def.args()[dim_idx].as<Variable>();
        if ((!var) || (var->name != dim)) {
            return false;
        }

        for (const auto &s : def.specializations()) {
            bool pure = is_dim_always_pure(s.definition, dim, dim_idx);
            if (!pure) {
                return false;
            }
        }
        return true;
    }

    void visit(const ProducerConsumer *op) {
        if (op->name != func.name()) {
            IRMutator::visit(op);
        } else {

            stmt = op;

            // We're interested in the case where exactly one of the
            // dimensions of the buffer has a min/extent that depends
            // on the loop_var.
            string dim = "";
            int dim_idx = 0;
            Expr min_required, max_required;

            debug(3) << "Considering sliding " << func.name()
                     << " along loop variable " << loop_var << "\n"
                     << "Region provided:\n";

            string prefix = func.name() + ".s" + std::to_string(func.updates().size()) + ".";
            const std::vector<string> func_args = func.args();
            for (int i = 0; i < func.dimensions(); i++) {
                // Look up the region required of this function's last stage
                string var = prefix + func_args[i];
                internal_assert(scope.contains(var + ".min") && scope.contains(var + ".max"));
                Expr min_req = scope.get(var + ".min");
                Expr max_req = scope.get(var + ".max");
                min_req = expand_expr(min_req, scope);
                max_req = expand_expr(max_req, scope);

                debug(3) << func_args[i] << ":" << min_req << ", " << max_req  << "\n";
                if (expr_depends_on_var(min_req, loop_var) ||
                    expr_depends_on_var(max_req, loop_var)) {
                    if (!dim.empty()) {
                        dim = "";
                        min_required = Expr();
                        max_required = Expr();
                        break;
                    } else {
                        dim = func_args[i];
                        dim_idx = i;
                        min_required = min_req;
                        max_required = max_req;
                    }
                }
            }

            if (!min_required.defined()) {
                debug(3) << "Could not perform sliding window optimization of "
                         << func.name() << " over " << loop_var << " because either zero "
                         << "or many dimensions of the function dependended on the loop var\n";
                return;
            }

            // If the function is not pure in the given dimension, give up. We also
            // need to make sure that it is pure in all the specializations
            bool pure = true;
            for (const Definition &def : func.updates()) {
                pure = is_dim_always_pure(def, dim, dim_idx);
                if (!pure) {
                    break;
                }
            }
            if (!pure) {
                debug(3) << "Could not performance sliding window optimization of "
                         << func.name() << " over " << loop_var << " because the function "
                         << "scatters along the related axis.\n";
                return;
            }

            bool can_slide_up = false;
            bool can_slide_down = false;

            Monotonic monotonic_min = is_monotonic(min_required, loop_var);
            Monotonic monotonic_max = is_monotonic(max_required, loop_var);

            if (monotonic_min == Monotonic::Increasing ||
                monotonic_min == Monotonic::Constant) {
                can_slide_up = true;
            }

            if (monotonic_max == Monotonic::Decreasing ||
                monotonic_max == Monotonic::Constant) {
                can_slide_down = true;
            }

            if (!can_slide_up && !can_slide_down) {
                debug(3) << "Not sliding " << func.name()
                         << " over dimension " << dim
                         << " along loop variable " << loop_var
                         << " because I couldn't prove it moved monotonically along that dimension\n"
                         << "Min is " << min_required << "\n"
                         << "Max is " << max_required << "\n";
                return;
            }

            // Ok, we've isolated a function, a dimension to slide
            // along, and loop variable to slide over.
            debug(3) << "Sliding " << func.name()
                     << " over dimension " << dim
                     << " along loop variable " << loop_var << "\n";

            Expr loop_var_expr = Variable::make(Int(32), loop_var);

            Expr prev_max_plus_one = substitute(loop_var, loop_var_expr - 1, max_required) + 1;
            Expr prev_min_minus_one = substitute(loop_var, loop_var_expr - 1, min_required) - 1;

            // If there's no overlap between adjacent iterations, we shouldn't slide.
            if (can_prove(min_required >= prev_max_plus_one) ||
                can_prove(max_required <= prev_min_minus_one)) {
                debug(3) << "Not sliding " << func.name()
                         << " over dimension " << dim
                         << " along loop variable " << loop_var
                         << " there's no overlap in the region computed across iterations\n"
                         << "Min is " << min_required << "\n"
                         << "Max is " << max_required << "\n";
                return;
            }

            Expr new_min, new_max;
            if (can_slide_up) {
                new_min = select(loop_var_expr <= loop_min, min_required, likely(prev_max_plus_one));
                new_max = max_required;
            } else {
                new_min = min_required;
                new_max = select(loop_var_expr <= loop_min, max_required, likely(prev_min_minus_one));
            }

            Expr early_stages_min_required = new_min;
            Expr early_stages_max_required = new_max;

            debug(3) << "Sliding " << func.name() << ", " << dim << "\n"
                     << "Pushing min up from " << min_required << " to " << new_min << "\n"
                     << "Shrinking max from " << max_required << " to " << new_max << "\n";

            // Now redefine the appropriate regions required
            if (can_slide_up) {
                replacements[prefix + dim + ".min"] = new_min;
            } else {
                replacements[prefix + dim + ".max"] = new_max;
            }

            for (size_t i = 0; i < func.updates().size(); i++) {
                string n = func.name() + ".s" + std::to_string(i) + "." + dim;
                replacements[n + ".min"] = Variable::make(Int(32), prefix + dim + ".min");
                replacements[n + ".max"] = Variable::make(Int(32), prefix + dim + ".max");
            }

            // Ok, we have a new min/max required and we're going to
            // rewrite all the lets that define bounds required. Now
            // we need to additionally expand the bounds required of
            // the last stage to cover values produced by stages
            // before the last one. Because, e.g., an intermediate
            // stage may be unrolled, expanding its bounds provided.
            if (op->update.defined()) {
                Box b = box_provided(op->produce, func.name());
                merge_boxes(b, box_provided(op->update, func.name()));
                if (can_slide_up) {
                    string n = prefix + dim + ".min";
                    Expr var = Variable::make(Int(32), n);
                    stmt = LetStmt::make(n, min(var, b[dim_idx].min), stmt);
                } else {
                    string n = prefix + dim + ".max";
                    Expr var = Variable::make(Int(32), n);
                    stmt = LetStmt::make(n, max(var, b[dim_idx].max), stmt);
                }
            }

        }
    }

    void visit(const For *op) {
        // It's not safe to enter an inner loop whose bounds depend on
        // the var we're sliding over.
        Expr min = expand_expr(op->min, scope);
        Expr extent = expand_expr(op->extent, scope);
        if (is_one(extent)) {
            // Just treat it like a let
            Stmt s = LetStmt::make(op->name, min, op->body);
            s = mutate(s);
            // Unpack it back into the for
            const LetStmt *l = s.as<LetStmt>();
            internal_assert(l);
            stmt = For::make(op->name, op->min, op->extent, op->for_type, op->device_api, l->body);
        } else if (is_monotonic(min, loop_var) != Monotonic::Constant ||
                   is_monotonic(extent, loop_var) != Monotonic::Constant) {
            debug(3) << "Not entering loop over " << op->name
                     << " because the bounds depend on the var we're sliding over: "
                     << min << ", " << extent << "\n";
            stmt = op;
        } else {
            IRMutator::visit(op);

        }
    }

    void visit(const LetStmt *op) {
        scope.push(op->name, simplify(expand_expr(op->value, scope)));
        Stmt new_body = mutate(op->body);

        Expr value = op->value;

        map<string, Expr>::iterator iter = replacements.find(op->name);
        if (iter != replacements.end()) {
            value = iter->second;
            replacements.erase(iter);
        }

        if (new_body.same_as(op->body) && value.same_as(op->value)) {
            stmt = op;
        } else {
            stmt = LetStmt::make(op->name, value, new_body);
        }
        scope.pop(op->name);
    }

public:
    SlidingWindowOnFunctionAndLoop(Function f, string v, Expr v_min) : func(f), loop_var(v), loop_min(v_min) {}
};

// Perform sliding window optimization for a particular function
class SlidingWindowOnFunction : public IRMutator {
    Function func;

    using IRMutator::visit;

    void visit(const For *op) {
        debug(3) << " Doing sliding window analysis over loop: " << op->name << "\n";

        Stmt new_body = op->body;

        new_body = mutate(new_body);

        if (op->for_type == ForType::Serial ||
            op->for_type == ForType::Unrolled) {
            new_body = SlidingWindowOnFunctionAndLoop(func, op->name, op->min).mutate(new_body);
        }

        if (new_body.same_as(op->body)) {
            stmt = op;
        } else {
            stmt = For::make(op->name, op->min, op->extent, op->for_type, op->device_api, new_body);
        }
    }

public:
    SlidingWindowOnFunction(Function f) : func(f) {}
};

// Perform sliding window optimization for all functions
class SlidingWindow : public IRMutator {
    const map<string, Function> &env;

    using IRMutator::visit;

    void visit(const Realize *op) {
        // Find the args for this function
        map<string, Function>::const_iterator iter = env.find(op->name);

        // If it's not in the environment it's some anonymous
        // realization that we should skip (e.g. an inlined reduction)
        if (iter == env.end()) {
            IRMutator::visit(op);
            return;
        }

        // If the Function in question has the same compute_at level
        // as its store_at level, skip it.
        const Schedule &sched = iter->second.schedule();
        if (sched.compute_level() == sched.store_level()) {
            IRMutator::visit(op);
            return;
        }

        Stmt new_body = op->body;

        debug(3) << "Doing sliding window analysis on realization of " << op->name << "\n";

        new_body = SlidingWindowOnFunction(iter->second).mutate(new_body);

        new_body = mutate(new_body);

        if (new_body.same_as(op->body)) {
            stmt = op;
        } else {
            stmt = Realize::make(op->name, op->types, op->bounds, op->condition, new_body);
        }
    }
public:
    SlidingWindow(const map<string, Function> &e) : env(e) {}

};

Stmt sliding_window(Stmt s, const map<string, Function> &env) {
    return SlidingWindow(env).mutate(s);
}

}
}
<commit_msg>Use likely_if_innermost for sliding window warmup branch.<commit_after>#include "SlidingWindow.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Scope.h"
#include "Debug.h"
#include "Substitute.h"
#include "IRPrinter.h"
#include "Simplify.h"
#include "Monotonic.h"
#include "Bounds.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;

namespace {

// Does an expression depend on a particular variable?
class ExprDependsOnVar : public IRVisitor {
    using IRVisitor::visit;

    void visit(const Variable *op) {
        if (op->name == var) result = true;
    }

    void visit(const Let *op) {
        op->value.accept(this);
        // The name might be hidden within the body of the let, in
        // which case there's no point descending.
        if (op->name != var) {
            op->body.accept(this);
        }
    }
public:

    bool result;
    string var;

    ExprDependsOnVar(string v) : result(false), var(v) {
    }
};

bool expr_depends_on_var(Expr e, string v) {
    ExprDependsOnVar depends(v);
    e.accept(&depends);
    return depends.result;
}

class ExpandExpr : public IRMutator {
    using IRMutator::visit;
    const Scope<Expr> &scope;

    void visit(const Variable *var) {
        if (scope.contains(var->name)) {
            expr = scope.get(var->name);
            debug(3) << "Fully expanded " << var->name << " -> " << expr << "\n";
        } else {
            expr = var;
        }
    }

public:
    ExpandExpr(const Scope<Expr> &s) : scope(s) {}

};

// Perform all the substitutions in a scope
Expr expand_expr(Expr e, const Scope<Expr> &scope) {
    ExpandExpr ee(scope);
    Expr result = ee.mutate(e);
    debug(3) << "Expanded " << e << " into " << result << "\n";
    return result;
}

}

// Perform sliding window optimization for a function over a
// particular serial for loop
class SlidingWindowOnFunctionAndLoop : public IRMutator {
    Function func;
    string loop_var;
    Expr loop_min;
    Scope<Expr> scope;

    map<string, Expr> replacements;

    using IRMutator::visit;

    // Check if the dimension at index 'dim_idx' is always pure (i.e. equal to 'dim')
    // in the definition (including in its specializations)
    bool is_dim_always_pure(const Definition &def, const string& dim, int dim_idx) {
        const Variable *var = def.args()[dim_idx].as<Variable>();
        if ((!var) || (var->name != dim)) {
            return false;
        }

        for (const auto &s : def.specializations()) {
            bool pure = is_dim_always_pure(s.definition, dim, dim_idx);
            if (!pure) {
                return false;
            }
        }
        return true;
    }

    void visit(const ProducerConsumer *op) {
        if (op->name != func.name()) {
            IRMutator::visit(op);
        } else {

            stmt = op;

            // We're interested in the case where exactly one of the
            // dimensions of the buffer has a min/extent that depends
            // on the loop_var.
            string dim = "";
            int dim_idx = 0;
            Expr min_required, max_required;

            debug(3) << "Considering sliding " << func.name()
                     << " along loop variable " << loop_var << "\n"
                     << "Region provided:\n";

            string prefix = func.name() + ".s" + std::to_string(func.updates().size()) + ".";
            const std::vector<string> func_args = func.args();
            for (int i = 0; i < func.dimensions(); i++) {
                // Look up the region required of this function's last stage
                string var = prefix + func_args[i];
                internal_assert(scope.contains(var + ".min") && scope.contains(var + ".max"));
                Expr min_req = scope.get(var + ".min");
                Expr max_req = scope.get(var + ".max");
                min_req = expand_expr(min_req, scope);
                max_req = expand_expr(max_req, scope);

                debug(3) << func_args[i] << ":" << min_req << ", " << max_req  << "\n";
                if (expr_depends_on_var(min_req, loop_var) ||
                    expr_depends_on_var(max_req, loop_var)) {
                    if (!dim.empty()) {
                        dim = "";
                        min_required = Expr();
                        max_required = Expr();
                        break;
                    } else {
                        dim = func_args[i];
                        dim_idx = i;
                        min_required = min_req;
                        max_required = max_req;
                    }
                }
            }

            if (!min_required.defined()) {
                debug(3) << "Could not perform sliding window optimization of "
                         << func.name() << " over " << loop_var << " because either zero "
                         << "or many dimensions of the function dependended on the loop var\n";
                return;
            }

            // If the function is not pure in the given dimension, give up. We also
            // need to make sure that it is pure in all the specializations
            bool pure = true;
            for (const Definition &def : func.updates()) {
                pure = is_dim_always_pure(def, dim, dim_idx);
                if (!pure) {
                    break;
                }
            }
            if (!pure) {
                debug(3) << "Could not performance sliding window optimization of "
                         << func.name() << " over " << loop_var << " because the function "
                         << "scatters along the related axis.\n";
                return;
            }

            bool can_slide_up = false;
            bool can_slide_down = false;

            Monotonic monotonic_min = is_monotonic(min_required, loop_var);
            Monotonic monotonic_max = is_monotonic(max_required, loop_var);

            if (monotonic_min == Monotonic::Increasing ||
                monotonic_min == Monotonic::Constant) {
                can_slide_up = true;
            }

            if (monotonic_max == Monotonic::Decreasing ||
                monotonic_max == Monotonic::Constant) {
                can_slide_down = true;
            }

            if (!can_slide_up && !can_slide_down) {
                debug(3) << "Not sliding " << func.name()
                         << " over dimension " << dim
                         << " along loop variable " << loop_var
                         << " because I couldn't prove it moved monotonically along that dimension\n"
                         << "Min is " << min_required << "\n"
                         << "Max is " << max_required << "\n";
                return;
            }

            // Ok, we've isolated a function, a dimension to slide
            // along, and loop variable to slide over.
            debug(3) << "Sliding " << func.name()
                     << " over dimension " << dim
                     << " along loop variable " << loop_var << "\n";

            Expr loop_var_expr = Variable::make(Int(32), loop_var);

            Expr prev_max_plus_one = substitute(loop_var, loop_var_expr - 1, max_required) + 1;
            Expr prev_min_minus_one = substitute(loop_var, loop_var_expr - 1, min_required) - 1;

            // If there's no overlap between adjacent iterations, we shouldn't slide.
            if (can_prove(min_required >= prev_max_plus_one) ||
                can_prove(max_required <= prev_min_minus_one)) {
                debug(3) << "Not sliding " << func.name()
                         << " over dimension " << dim
                         << " along loop variable " << loop_var
                         << " there's no overlap in the region computed across iterations\n"
                         << "Min is " << min_required << "\n"
                         << "Max is " << max_required << "\n";
                return;
            }

            Expr new_min, new_max;
            if (can_slide_up) {
                new_min = select(loop_var_expr <= loop_min, min_required, likely_if_innermost(prev_max_plus_one));
                new_max = max_required;
            } else {
                new_min = min_required;
                new_max = select(loop_var_expr <= loop_min, max_required, likely_if_innermost(prev_min_minus_one));
            }

            Expr early_stages_min_required = new_min;
            Expr early_stages_max_required = new_max;

            debug(3) << "Sliding " << func.name() << ", " << dim << "\n"
                     << "Pushing min up from " << min_required << " to " << new_min << "\n"
                     << "Shrinking max from " << max_required << " to " << new_max << "\n";

            // Now redefine the appropriate regions required
            if (can_slide_up) {
                replacements[prefix + dim + ".min"] = new_min;
            } else {
                replacements[prefix + dim + ".max"] = new_max;
            }

            for (size_t i = 0; i < func.updates().size(); i++) {
                string n = func.name() + ".s" + std::to_string(i) + "." + dim;
                replacements[n + ".min"] = Variable::make(Int(32), prefix + dim + ".min");
                replacements[n + ".max"] = Variable::make(Int(32), prefix + dim + ".max");
            }

            // Ok, we have a new min/max required and we're going to
            // rewrite all the lets that define bounds required. Now
            // we need to additionally expand the bounds required of
            // the last stage to cover values produced by stages
            // before the last one. Because, e.g., an intermediate
            // stage may be unrolled, expanding its bounds provided.
            if (op->update.defined()) {
                Box b = box_provided(op->produce, func.name());
                merge_boxes(b, box_provided(op->update, func.name()));
                if (can_slide_up) {
                    string n = prefix + dim + ".min";
                    Expr var = Variable::make(Int(32), n);
                    stmt = LetStmt::make(n, min(var, b[dim_idx].min), stmt);
                } else {
                    string n = prefix + dim + ".max";
                    Expr var = Variable::make(Int(32), n);
                    stmt = LetStmt::make(n, max(var, b[dim_idx].max), stmt);
                }
            }

        }
    }

    void visit(const For *op) {
        // It's not safe to enter an inner loop whose bounds depend on
        // the var we're sliding over.
        Expr min = expand_expr(op->min, scope);
        Expr extent = expand_expr(op->extent, scope);
        if (is_one(extent)) {
            // Just treat it like a let
            Stmt s = LetStmt::make(op->name, min, op->body);
            s = mutate(s);
            // Unpack it back into the for
            const LetStmt *l = s.as<LetStmt>();
            internal_assert(l);
            stmt = For::make(op->name, op->min, op->extent, op->for_type, op->device_api, l->body);
        } else if (is_monotonic(min, loop_var) != Monotonic::Constant ||
                   is_monotonic(extent, loop_var) != Monotonic::Constant) {
            debug(3) << "Not entering loop over " << op->name
                     << " because the bounds depend on the var we're sliding over: "
                     << min << ", " << extent << "\n";
            stmt = op;
        } else {
            IRMutator::visit(op);

        }
    }

    void visit(const LetStmt *op) {
        scope.push(op->name, simplify(expand_expr(op->value, scope)));
        Stmt new_body = mutate(op->body);

        Expr value = op->value;

        map<string, Expr>::iterator iter = replacements.find(op->name);
        if (iter != replacements.end()) {
            value = iter->second;
            replacements.erase(iter);
        }

        if (new_body.same_as(op->body) && value.same_as(op->value)) {
            stmt = op;
        } else {
            stmt = LetStmt::make(op->name, value, new_body);
        }
        scope.pop(op->name);
    }

public:
    SlidingWindowOnFunctionAndLoop(Function f, string v, Expr v_min) : func(f), loop_var(v), loop_min(v_min) {}
};

// Perform sliding window optimization for a particular function
class SlidingWindowOnFunction : public IRMutator {
    Function func;

    using IRMutator::visit;

    void visit(const For *op) {
        debug(3) << " Doing sliding window analysis over loop: " << op->name << "\n";

        Stmt new_body = op->body;

        new_body = mutate(new_body);

        if (op->for_type == ForType::Serial ||
            op->for_type == ForType::Unrolled) {
            new_body = SlidingWindowOnFunctionAndLoop(func, op->name, op->min).mutate(new_body);
        }

        if (new_body.same_as(op->body)) {
            stmt = op;
        } else {
            stmt = For::make(op->name, op->min, op->extent, op->for_type, op->device_api, new_body);
        }
    }

public:
    SlidingWindowOnFunction(Function f) : func(f) {}
};

// Perform sliding window optimization for all functions
class SlidingWindow : public IRMutator {
    const map<string, Function> &env;

    using IRMutator::visit;

    void visit(const Realize *op) {
        // Find the args for this function
        map<string, Function>::const_iterator iter = env.find(op->name);

        // If it's not in the environment it's some anonymous
        // realization that we should skip (e.g. an inlined reduction)
        if (iter == env.end()) {
            IRMutator::visit(op);
            return;
        }

        // If the Function in question has the same compute_at level
        // as its store_at level, skip it.
        const Schedule &sched = iter->second.schedule();
        if (sched.compute_level() == sched.store_level()) {
            IRMutator::visit(op);
            return;
        }

        Stmt new_body = op->body;

        debug(3) << "Doing sliding window analysis on realization of " << op->name << "\n";

        new_body = SlidingWindowOnFunction(iter->second).mutate(new_body);

        new_body = mutate(new_body);

        if (new_body.same_as(op->body)) {
            stmt = op;
        } else {
            stmt = Realize::make(op->name, op->types, op->bounds, op->condition, new_body);
        }
    }
public:
    SlidingWindow(const map<string, Function> &e) : env(e) {}

};

Stmt sliding_window(Stmt s, const map<string, Function> &env) {
    return SlidingWindow(env).mutate(s);
}

}
}
<|endoftext|>


<commit_before>// ToolbarHandler for fluxbox
// Copyright (c) 2003 Simon Bowden (rathnor at fluxbox.org)
//                and Henrik Kinnunen (fluxgen at fluxbox.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// $Id: ToolbarHandler.cc,v 1.2 2003/03/10 21:38:47 rathnor Exp $

/**
 * The ToolbarHandler class acts as a rough interface to the toolbar.
 * It deals with whether it should be there or not, so anything that
 * always needs to be accessible must come through the handler.
 */

#include "ToolbarHandler.hh"
#include "Window.hh"
#include "Screen.hh"
#include "Workspace.hh"
#include "MenuItem.hh"
#include "Menu.hh"
#include "FbCommands.hh"
#include "RefCount.hh"
#include "SimpleCommand.hh"
#include "MacroCommand.hh"
#include "IntResMenuItem.hh"
#include "BoolMenuItem.hh"

namespace {

class ToolbarModeMenuItem : public FbTk::MenuItem {
public:
    ToolbarModeMenuItem(const char *label, ToolbarHandler &handler, ToolbarHandler::ToolbarMode mode, FbTk::RefCount<FbTk::Command> &cmd):
        FbTk::MenuItem(label, cmd), m_handler(handler), m_mode(mode) {
    }
    bool isEnabled() const { return m_handler.getMode() != m_mode; }
    void click(int button, int time) {
        m_handler.setMode(m_mode);
        FbTk::MenuItem::click(button, time);
    }

private:
    ToolbarHandler &m_handler;
    ToolbarHandler::ToolbarMode m_mode;
};

void setupModeMenu(FbTk::Menu &menu, ToolbarHandler &handler) {
    //I18n *i18n = I18n::instance();
    //using namespace FBNLS;
    using namespace FbTk;

    RefCount<Command> saverc_cmd(new SimpleCommand<Fluxbox>(
        *Fluxbox::instance(),
        &Fluxbox::save_rc));

    //TODO: nls
    menu.insert(new ToolbarModeMenuItem("Off", handler,
                                        ToolbarHandler::OFF, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("None", handler,
                                        ToolbarHandler::NONE, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Icons", handler,
                                        ToolbarHandler::ICONS, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Workspace Icons", handler,
                                        ToolbarHandler::WORKSPACEICONS, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Workspace", handler,
                                        ToolbarHandler::WORKSPACE, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("All Windows", handler,
                                        ToolbarHandler::ALLWINDOWS, saverc_cmd));
    menu.update();
}

}; // end anonymous namespace

ToolbarHandler::ToolbarHandler(BScreen &screen, ToolbarMode mode)
    : m_screen(screen), m_mode(mode), m_toolbar(0), m_current_workspace(0),
      m_modemenu(*screen.menuTheme(),
                 screen.getScreenNumber(), *screen.getImageControl()),
      m_toolbarmenu(*screen.menuTheme(),
                 screen.getScreenNumber(), *screen.getImageControl())
{
    m_modemenu.setInternalMenu();
    setupModeMenu(m_modemenu, *this);
    setMode(mode, false); // the atomhandler part will initialise it shortly
}

void ToolbarHandler::setMode(ToolbarMode mode, bool initialise) {
    if (mode < 0 || mode >= LASTMODE || (mode == m_mode && initialise)) return;
    if (mode == OFF) {
        m_toolbarmenu.removeAll();
        //TODO: nls
        m_toolbarmenu.insert("Mode...", &m_modemenu);
        m_toolbar.reset(0);
        m_toolbarmenu.update();
        return;
    } else if (!m_toolbar.get()) {
        m_toolbarmenu.removeAll();

        m_toolbarmenu.insert("Mode...", &m_modemenu);
        m_toolbar.reset(new Toolbar(m_screen,
                                    *m_screen.layerManager().getLayer(m_screen.getToolbarLayerNum()), m_toolbarmenu));

    }

    if (mode == NONE) {
        // disableIconBar will clean up
        m_toolbar->disableIconBar();
    } else {
        // rebuild it
        // be sure the iconbar is on
        m_toolbar->enableIconBar();
        m_toolbar->delAllIcons();
    }
    // reset Toolbar, and reload it (initForScreen)
    m_mode = mode;
    if (initialise)
        initForScreen(m_screen);
}

void ToolbarHandler::initForScreen(BScreen &screen) {
    if (&m_screen != &screen) return;
    switch (m_mode) {
    case OFF:
        break;
    case NONE:
        break;
    case ALLWINDOWS:
    {
        BScreen::Workspaces::const_iterator workspace_it = m_screen.getWorkspacesList().begin();
        BScreen::Workspaces::const_iterator workspace_it_end = m_screen.getWorkspacesList().end();
        for (; workspace_it != workspace_it_end; ++workspace_it) {
            Workspace::Windows &wins = (*workspace_it)->getWindowList();
            Workspace::Windows::iterator wit = wins.begin();
            Workspace::Windows::iterator wit_end = wins.end();
            for (; wit != wit_end; ++wit) {
                m_toolbar->addIcon(*wit);
            }
        }
    }
    // fall through and add icons
    case LASTMODE:
    case ICONS:
    {
        BScreen::Icons &iconlist = m_screen.getIconList();
        BScreen::Icons::iterator iconit = iconlist.begin();
        BScreen::Icons::iterator iconit_end = iconlist.end();
        for(; iconit != iconit_end; ++iconit) {
            m_toolbar->addIcon(*iconit);
        }
    }
    break;
    case WORKSPACE:
    {
        Workspace::Windows &wins = m_screen.getCurrentWorkspace()->getWindowList();
        Workspace::Windows::iterator wit = wins.begin();
        Workspace::Windows::iterator wit_end = wins.end();
        for (; wit != wit_end; ++wit) {
            m_toolbar->addIcon(*wit);
        }
    }
    // fall through and add icons for this workspace
    case WORKSPACEICONS:
    {
        m_current_workspace = m_screen.getCurrentWorkspaceID();

        BScreen::Icons &wiconlist = m_screen.getIconList();
        BScreen::Icons::iterator iconit = wiconlist.begin();
        BScreen::Icons::iterator iconit_end = wiconlist.end();
        for(; iconit != iconit_end; ++iconit) {
            if ((*iconit)->getWorkspaceNumber() == m_current_workspace)
                m_toolbar->addIcon(*iconit);
        }
    }
    break;
    }
}

void ToolbarHandler::setupWindow(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;
    switch (m_mode) {
    case OFF:
    case NONE:
        break;
    case WORKSPACE:
        if (win.getWorkspaceNumber() == m_current_workspace)
            m_toolbar->addIcon(&win);
        break;
    case WORKSPACEICONS:
        if (win.getWorkspaceNumber() != m_current_workspace)
            break;
        // else fall through and add the icon
    case LASTMODE:
    case ICONS:
        if (win.isIconic()) {
            m_toolbar->addIcon(&win);
        }
        break;
    case ALLWINDOWS:
        m_toolbar->addIcon(&win);
        break;
    }
}

void ToolbarHandler::updateWindowClose(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;
    // check status of window (in current workspace, etc) and remove if necessary
    switch (m_mode) {
    case OFF:
    case NONE:
        break;
    case WORKSPACEICONS:
        if (win.getWorkspaceNumber() != m_current_workspace)
            break;
        // else fall through and remove the icon
    case LASTMODE:
    case ICONS:
        if (win.isIconic()) {
            m_toolbar->delIcon(&win);
        }
        break;
    case WORKSPACE:
        if (win.getWorkspaceNumber() == m_current_workspace)
            m_toolbar->delIcon(&win);
        break;
    case ALLWINDOWS:
        m_toolbar->delIcon(&win);
        break;
    }
}

void ToolbarHandler::updateState(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;

    // this function only relevant for icons
    switch (m_mode) {
    case OFF:
    case NONE:
    case WORKSPACE:
    case ALLWINDOWS:
        break;
    case WORKSPACEICONS:
        if (win.getWorkspaceNumber() != m_current_workspace) break;
        // else fall through and do the same as icons (knowing it is the right ws)
    case LASTMODE:
    case ICONS:
        // if the window is iconic (it mustn't have been before), then add it
        // else remove it
        if (win.isIconic()) {
            if (!m_toolbar->containsIcon(win)) {
                m_toolbar->addIcon(&win);
            }
        } else {
            m_toolbar->delIcon(&win);
        }
        break;
    }
}

void ToolbarHandler::updateWorkspace(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;
    // don't care about current workspace except if in workspace mode
    if (m_mode != WORKSPACE) return;
    if (win.getWorkspaceNumber() == m_current_workspace) {
        m_toolbar->addIcon(&win);
    } else {
        // relies on the fact that this runs but does nothing if window isn't contained.
        m_toolbar->delIcon(&win);
    }
}

void ToolbarHandler::updateCurrentWorkspace(BScreen &screen) {
    if (&screen != &m_screen) return;
    // if only displaying current workspace, update list
    // otherwise ignore it
    if (m_mode != WORKSPACE && m_mode != WORKSPACEICONS) return;
    m_toolbar->delAllIcons();
    initForScreen(m_screen);
}

<commit_msg>workaround another bug until can be rearranged properly<commit_after>// ToolbarHandler for fluxbox
// Copyright (c) 2003 Simon Bowden (rathnor at fluxbox.org)
//                and Henrik Kinnunen (fluxgen at fluxbox.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// $Id: ToolbarHandler.cc,v 1.3 2003/03/10 22:25:18 rathnor Exp $

/**
 * The ToolbarHandler class acts as a rough interface to the toolbar.
 * It deals with whether it should be there or not, so anything that
 * always needs to be accessible must come through the handler.
 */

#include "ToolbarHandler.hh"
#include "Window.hh"
#include "Screen.hh"
#include "Workspace.hh"
#include "MenuItem.hh"
#include "Menu.hh"
#include "FbCommands.hh"
#include "RefCount.hh"
#include "SimpleCommand.hh"
#include "MacroCommand.hh"
#include "IntResMenuItem.hh"
#include "BoolMenuItem.hh"

namespace {

class ToolbarModeMenuItem : public FbTk::MenuItem {
public:
    ToolbarModeMenuItem(const char *label, ToolbarHandler &handler, ToolbarHandler::ToolbarMode mode, FbTk::RefCount<FbTk::Command> &cmd):
        FbTk::MenuItem(label, cmd), m_handler(handler), m_mode(mode) {
    }
    bool isEnabled() const { return m_handler.getMode() != m_mode; }
    void click(int button, int time) {
        m_handler.setMode(m_mode);
        FbTk::MenuItem::click(button, time);
    }

private:
    ToolbarHandler &m_handler;
    ToolbarHandler::ToolbarMode m_mode;
};

void setupModeMenu(FbTk::Menu &menu, ToolbarHandler &handler) {
    //I18n *i18n = I18n::instance();
    //using namespace FBNLS;
    using namespace FbTk;

    RefCount<Command> saverc_cmd(new SimpleCommand<Fluxbox>(
        *Fluxbox::instance(),
        &Fluxbox::save_rc));

    //TODO: nls
    menu.insert(new ToolbarModeMenuItem("Off", handler,
                                        ToolbarHandler::OFF, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("None", handler,
                                        ToolbarHandler::NONE, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Icons", handler,
                                        ToolbarHandler::ICONS, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Workspace Icons", handler,
                                        ToolbarHandler::WORKSPACEICONS, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Workspace", handler,
                                        ToolbarHandler::WORKSPACE, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("All Windows", handler,
                                        ToolbarHandler::ALLWINDOWS, saverc_cmd));
    menu.update();
}

}; // end anonymous namespace

ToolbarHandler::ToolbarHandler(BScreen &screen, ToolbarMode mode)
    : m_screen(screen), m_mode(mode), m_toolbar(0), m_current_workspace(0),
      m_modemenu(*screen.menuTheme(),
                 screen.getScreenNumber(), *screen.getImageControl()),
      m_toolbarmenu(*screen.menuTheme(),
                 screen.getScreenNumber(), *screen.getImageControl())
{
    m_modemenu.setInternalMenu();
    setupModeMenu(m_modemenu, *this);
    setMode(mode, false); // the atomhandler part will initialise it shortly
}

void ToolbarHandler::setMode(ToolbarMode mode, bool initialise) {
    if (mode < 0 || mode >= LASTMODE || (mode == m_mode && initialise)) return;
    if (mode == OFF) {
        m_toolbarmenu.removeAll();
        //TODO: nls
        m_toolbarmenu.insert("Mode...", &m_modemenu);
        m_toolbar.reset(0);
        m_toolbarmenu.update();
        return;
    } else if (!m_toolbar.get()) {
        m_toolbarmenu.removeAll();

        m_toolbarmenu.insert("Mode...", &m_modemenu);
        m_toolbar.reset(new Toolbar(m_screen,
                                    *m_screen.layerManager().getLayer(m_screen.getToolbarLayerNum()), m_toolbarmenu));

    }

    if (mode == NONE) {
        // disableIconBar will clean up
        m_toolbar->disableIconBar();
    } else {
        // rebuild it
        // be sure the iconbar is on
        m_toolbar->enableIconBar();
        m_toolbar->delAllIcons();
    }
    // reset Toolbar, and reload it (initForScreen)
    m_mode = mode;
    if (initialise)
        initForScreen(m_screen);
}

void ToolbarHandler::initForScreen(BScreen &screen) {
    if (&m_screen != &screen) return;
    switch (m_mode) {
    case OFF:
        break;
    case NONE:
        break;
    case ALLWINDOWS:
    {
        BScreen::Workspaces::const_iterator workspace_it = m_screen.getWorkspacesList().begin();
        BScreen::Workspaces::const_iterator workspace_it_end = m_screen.getWorkspacesList().end();
        for (; workspace_it != workspace_it_end; ++workspace_it) {
            Workspace::Windows &wins = (*workspace_it)->getWindowList();
            Workspace::Windows::iterator wit = wins.begin();
            Workspace::Windows::iterator wit_end = wins.end();
            for (; wit != wit_end; ++wit) {
                m_toolbar->addIcon(*wit);
            }
        }
    }
    // fall through and add icons
    case LASTMODE:
    case ICONS:
    {
        BScreen::Icons &iconlist = m_screen.getIconList();
        BScreen::Icons::iterator iconit = iconlist.begin();
        BScreen::Icons::iterator iconit_end = iconlist.end();
        for(; iconit != iconit_end; ++iconit) {
            m_toolbar->addIcon(*iconit);
        }
    }
    break;
    case WORKSPACE:
    {
        Workspace::Windows &wins = m_screen.getCurrentWorkspace()->getWindowList();
        Workspace::Windows::iterator wit = wins.begin();
        Workspace::Windows::iterator wit_end = wins.end();
        for (; wit != wit_end; ++wit) {
            m_toolbar->addIcon(*wit);
        }
    }
    // fall through and add icons for this workspace
    case WORKSPACEICONS:
    {
        m_current_workspace = m_screen.getCurrentWorkspaceID();

        BScreen::Icons &wiconlist = m_screen.getIconList();
        BScreen::Icons::iterator iconit = wiconlist.begin();
        BScreen::Icons::iterator iconit_end = wiconlist.end();
        for(; iconit != iconit_end; ++iconit) {
            if ((*iconit)->getWorkspaceNumber() == m_current_workspace)
                m_toolbar->addIcon(*iconit);
        }
    }
    break;
    }
}

void ToolbarHandler::setupWindow(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;
    switch (m_mode) {
    case OFF:
    case NONE:
        break;
    case WORKSPACE:
        if (win.getWorkspaceNumber() == m_current_workspace)
            m_toolbar->addIcon(&win);
        break;
    case WORKSPACEICONS:
        if (win.getWorkspaceNumber() != m_current_workspace)
            break;
        // else fall through and add the icon
    case LASTMODE:
    case ICONS:
        if (win.isIconic()) {
            m_toolbar->addIcon(&win);
        }
        break;
    case ALLWINDOWS:
        m_toolbar->addIcon(&win);
        break;
    }
}

void ToolbarHandler::updateWindowClose(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;
    // check status of window (in current workspace, etc) and remove if necessary
    switch (m_mode) {
    case OFF:
    case NONE:
        break;
    case WORKSPACEICONS:
        if (win.getWorkspaceNumber() != m_current_workspace)
            break;
        // else fall through and remove the icon
    case LASTMODE:
    case ICONS:
        if (win.isIconic()) {
            m_toolbar->delIcon(&win);
        }
        break;
    case WORKSPACE:
        if (win.getWorkspaceNumber() == m_current_workspace)
            m_toolbar->delIcon(&win);
        break;
    case ALLWINDOWS:
        m_toolbar->delIcon(&win);
        break;
    }
}

void ToolbarHandler::updateState(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;

    // this function only relevant for icons
    switch (m_mode) {
    case OFF:
    case NONE:
    case WORKSPACE:
    case ALLWINDOWS:
        break;
    case WORKSPACEICONS:
        if (win.getWorkspaceNumber() != m_current_workspace) break;
        // else fall through and do the same as icons (knowing it is the right ws)
    case LASTMODE:
    case ICONS:
        // if the window is iconic (it mustn't have been before), then add it
        // else remove it
        if (win.isIconic()) {
            if (!m_toolbar->containsIcon(win)) {
                m_toolbar->addIcon(&win);
            }
        } else {
            m_toolbar->delIcon(&win);
        }
        break;
    }
}

void ToolbarHandler::updateWorkspace(FluxboxWindow &win) {
    if (win.getScreen() != &m_screen) return;
    // don't care about current workspace except if in workspace mode
    if (!(m_mode == WORKSPACE || (m_mode == WORKSPACEICONS && win.isIconic()))) return;

    if (win.getWorkspaceNumber() == m_current_workspace) {
        // TODO
        // this shouldn't be needed, but is until Workspaces get fixed so that
        // you only move between them, you don't 'add' and 'remove'
        // alternatively, fix reassocaiteWindow so that the iconic stuff is
        // done elsewhere
        if (!m_toolbar->containsIcon(win))
            m_toolbar->addIcon(&win);
    } else {
        // relies on the fact that this runs but does nothing if window isn't contained.
        m_toolbar->delIcon(&win);
    }
}

void ToolbarHandler::updateCurrentWorkspace(BScreen &screen) {
    if (&screen != &m_screen) return;
    // if only displaying current workspace, update list
    // otherwise ignore it
    if (m_mode != WORKSPACE && m_mode != WORKSPACEICONS) return;
    m_toolbar->delAllIcons();
    initForScreen(m_screen);
}

<|endoftext|>


<commit_before>/*
Given an integer n, return a string array answer (1-indexed) where:

answer[i] == "FizzBuzz" if i is divisible by 3 and 5.
answer[i] == "Fizz" if i is divisible by 3.
answer[i] == "Buzz" if i is divisible by 5.
answer[i] == i (as a string) if none of the above conditions are true.
*/
#include <string>
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

using namespace std;
class Solution {
public:
    vector<string> fizzBuzz(int n) {
        vector<string> ans;
        for (int i = 1; i <= n; i++) {
            if ((i % 3 == 0) && (i % 5 == 0)) {
                ans.push_back("FizzBuzz");
            } else if (i % 3 == 0) {
                ans.push_back("Fizz");
            } else if (i % 5 == 0) {
                ans.push_back("Buzz");
            } else {
                ans.push_back(to_string(i));
            }
        }
        return ans;
    }
};

int main() {
	int test[] = { 1, 7, 4, 6, 3, 10, 2 };
    Solution sol;
    auto fizzBuzz = sol.fizzBuzz(15);

    cout << "Fizzbuzz" << endl;
    std::ostream_iterator<string> out_it (std::cout,", ");
    copy ( fizzBuzz.begin(), fizzBuzz.end(), out_it );
	return 0;
}<commit_msg>Shuffle<commit_after>/*
Given an integer n, return a string array answer (1-indexed) where:

answer[i] == "FizzBuzz" if i is divisible by 3 and 5.
answer[i] == "Fizz" if i is divisible by 3.
answer[i] == "Buzz" if i is divisible by 5.
answer[i] == i (as a string) if none of the above conditions are true.
*/
#include <string>
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

using namespace std;
class Solution {
public:
    vector<string> fizzBuzz(int n) {
        vector<string> ans;
        for (int i = 1; i <= n; i++) {
            if ((i % 3 == 0) && (i % 5 == 0)) {
                ans.push_back("FizzBuzz");
            } else if (i % 3 == 0) {
                ans.push_back("Fizz");
            } else if (i % 5 == 0) {
                ans.push_back("Buzz");
            } else {
                ans.push_back(to_string(i));
            }
        }
        return ans;
    }
};

/*
Given an integer array nums, design an algorithm to randomly shuffle the array. All permutations of the array should be equally likely as a result of the shuffling.

Implement the Solution class:

Solution(int[] nums) Initializes the object with the integer array nums.
int[] reset() Resets the array to its original configuration and returns it.
int[] shuffle() Returns a random shuffling of the array.

Input
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
Output
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

Explanation
Solution solution = new Solution([1, 2, 3]);
solution.shuffle();    // Shuffle the array [1,2,3] and return its result.
                       // Any permutation of [1,2,3] must be equally likely to be returned.
                       // Example: return [3, 1, 2]
solution.reset();      // Resets the array back to its original configuration [1,2,3]. Return [1, 2, 3]
solution.shuffle();    // Returns the random shuffling of array [1,2,3]. Example: return [1, 3, 2]

Note that the input is a ref, not const so you must change the passed in deck
*/

#include <algorithm>
#include <random>       // std::default_random_engine
#include <chrono>       // std::chrono::system_clock

class ShuffleSolution {
public:
    ShuffleSolution(vector<int>& nums) : _originalNums(nums) {
         // std::vector<int> ivec(nums.size());
        //std::iota(ivec.begin(), ivec.end(), 0);
        _positions = _originalNums;
    }

    vector<int> reset() {
        _originalNums = _positions;
        return _originalNums;
    }

    vector<int> shuffle() {
        auto seed = std::chrono::system_clock::now().time_since_epoch().count();
        std::shuffle(_originalNums.begin(), _originalNums.end(), std::default_random_engine(seed));
        return _originalNums;
    }
private:
    vector<int> &_originalNums;
    vector<int> _positions;
};

/**
 * Your Solution object will be instantiated and called as such:
 * Solution* obj = new Solution(nums);
 * vector<int> param_1 = obj->reset();
 * vector<int> param_2 = obj->shuffle();
 */

int main() {
	int test[] = { 1, 7, 4, 6, 3, 10, 2 };
    Solution sol;
    auto fizzBuzz = sol.fizzBuzz(15);

    cout << "Fizzbuzz" << endl;
    std::ostream_iterator<string> out_it (std::cout,", ");
    copy ( fizzBuzz.begin(), fizzBuzz.end(), out_it );
	return 0;
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017-2018 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include "poll_manager.h"
#include "de_web_plugin_private.h"

/*! Constructor.
 */
PollManager::PollManager(QObject *parent) :
    QObject(parent)
{
    pollState = StateIdle;
    timer = new QTimer(this);
    timer->setSingleShot(true);
    connect(timer, SIGNAL(timeout()), this, SLOT(pollTimerFired()));
    plugin = qobject_cast<DeRestPluginPrivate*>(parent);
}

/*! Queues polling of the node.
    \param restNode - the node to poll
 */
void PollManager::poll(RestNodeBase *restNode, const QDateTime &tStart)
{
    Resource *r = dynamic_cast<Resource*>(restNode);
    DBG_Assert(r);
    if (!r || !restNode->node())
    {
        return;
    }

    PollItem pitem;

    if (!restNode->node()->nodeDescriptor().receiverOnWhenIdle())
    {
        return;
    }

    LightNode *lightNode = nullptr;
    Sensor *sensor = nullptr;

    if (r->prefix() == RLights)
    {
        lightNode = dynamic_cast<LightNode*>(restNode);
        DBG_Assert(lightNode);
        pitem.endpoint = lightNode->haEndpoint().endpoint();
        DBG_Printf(DBG_INFO_L2, ">>>> Poll light node %s\n", qPrintable(lightNode->name()));
    }
    else if (r->prefix() == RSensors)
    {
        sensor = dynamic_cast<Sensor*>(restNode);
        DBG_Assert(sensor);
        pitem.endpoint = sensor->fingerPrint().endpoint;
        DBG_Printf(DBG_INFO_L2, ">>>> Poll %s sensor node %s\n", qPrintable(sensor->type()), qPrintable(sensor->name()));
    }
    else
    {
        return;
    }

    pitem.id = restNode->id();
    pitem.prefix = r->prefix();
    pitem.address = restNode->address();
    pitem.tStart = tStart;

    for (int i = 0; i < r->itemCount(); i++)
    {
        const ResourceItem *item = r->itemForIndex(i);
        const char *suffix = item ? item->descriptor().suffix : nullptr;

        if (suffix == RStateOn ||
            suffix == RStateBri ||
            suffix == RStateColorMode ||
            (suffix == RStateConsumption && sensor && sensor->type() == QLatin1String("ZHAConsumption")) ||
            (suffix == RStatePower && sensor && sensor->type() == QLatin1String("ZHAPower")) ||
            (suffix == RStatePresence && sensor && sensor->type() == QLatin1String("ZHAPresence")) ||
            (suffix == RStateLightLevel && sensor && sensor->type() == QLatin1String("ZHALightLevel")) ||
            suffix == RAttrModelId ||
            suffix == RAttrSwVersion)
        {
            // DBG_Printf(DBG_INFO_L2, "    attribute %s\n", suffix);
            pitem.items.push_back(suffix);
        }
    }

    for (PollItem &i : items)
    {
        if (i.prefix == r->prefix() && i.id == restNode->id())
        {
            i.items = pitem.items; // update
            if (tStart.isValid())
            {
                i.tStart = tStart;
            }
            return;
        }
    }

    items.push_back(pitem);

    if (!timer->isActive())
    {
        timer->start(1);
    }
}

/*! Delays polling for \p ms milliseconds.
 */
void PollManager::delay(int ms)
{
    timer->stop();
    timer->start(ms);
}

/*! Handle APS confirm if related to polling.
 */
void PollManager::apsdeDataConfirm(const deCONZ::ApsDataConfirm &conf)
{
    if (pollState != StateWait)
    {
        return;
    }

    if (apsReqId != conf.id())
    {
        return;
    }

    if (dstAddr.hasExt() && conf.dstAddress().hasExt()
        && dstAddr.ext() != conf.dstAddress().ext())
    {

    }

    else if (dstAddr.hasNwk() && conf.dstAddress().hasNwk()
        && dstAddr.nwk() != conf.dstAddress().nwk())
    {

    }

    DBG_Printf(DBG_INFO_L2, "Poll APS confirm %u status: 0x%02X\n", conf.id(), conf.status());

    pollState = StateIdle;
    timer->stop();
    timer->start(1);
}

/*! Timer callback to proceed polling.
 */
void PollManager::pollTimerFired()
{
    if (pollState == StateWait)
    {
        DBG_Printf(DBG_INFO, "timeout on poll APS confirm\n");
        pollState = StateIdle;
    }

    DBG_Assert(pollState == StateIdle);

    if (items.empty())
    {
        return;
    }

    QDateTime now = QDateTime::currentDateTime();
    PollItem &pitem = items.front();
    Resource *r = plugin->getResource(pitem.prefix, pitem.id);
    ResourceItem *item = nullptr;
    RestNodeBase *restNode = nullptr;
    const LightNode *lightNode = nullptr;
    if (r && r->prefix() == RLights)
    {
        restNode = plugin->getLightNodeForId(pitem.id);
        lightNode = dynamic_cast<LightNode*>(restNode);
        item = r->item(RStateReachable);
    }
    else if (r && r->prefix() == RSensors)
    {
        restNode = plugin->getSensorNodeForId(pitem.id);
        item = r->item(RConfigReachable);
    }

    if (pitem.tStart.isValid() && pitem.tStart > now)
    {
        if (items.size() > 1)
        {
            PollItem tmp = pitem;
            items.front() = items.back();
            items.back() = tmp;
        }
        timer->start(1);
        return;
    }

    if (!r || pitem.items.empty() ||
        !restNode ||
        //!restNode->lastRx().isValid() ||
        !item || !item->toBool()) // not reachable
    {
        items.front() = items.back();
        items.pop_back();
        timer->start(1);
        return;
    }

    const auto dtReachable = item->lastSet().secsTo(now);

    quint16 clusterId = 0xffff; // invalid
    std::vector<quint16> attributes;

    item = r->item(RStateOn);
    bool isOn = item ? item->toBool() : false;
    const char *&suffix = pitem.items[0];

    for (size_t i = 0; pitem.items[0] == nullptr && i < pitem.items.size(); i++)
    {
        if (pitem.items[i] != nullptr)
        {
            pitem.items[0] = pitem.items[i]; // move to front
            pitem.items[i] = nullptr; // clear
            break;
        }
    }

    if (!suffix)
    {
        pitem.items.clear(); // all done
    }

    if (suffix == RStateOn)
    {
        if (lightNode && lightNode->manufacturerCode() != VENDOR_115F) // reports
        {
            clusterId = ONOFF_CLUSTER_ID;
            attributes.push_back(0x0000); // onOff
        }
    }
    else if (suffix == RStateBri && isOn)
    {
        NodeValue &val = restNode->getZclValue(LEVEL_CLUSTER_ID, 0x0000);

        if (isOn || !val.timestamp.isValid())
        {
            clusterId = LEVEL_CLUSTER_ID;
            attributes.push_back(0x0000); // current level
        }
    }
    else if (suffix == RStateColorMode && lightNode)
    {
        clusterId = COLOR_CLUSTER_ID;
        item = r->item(RConfigColorCapabilities);

        if (!item || item->toNumber() <= 0)
        {
            attributes.push_back(0x0008); // color mode
            attributes.push_back(0x4001); // enhanced color mode
            attributes.push_back(0x400a); // color capabilities
            attributes.push_back(0x400b); // color temperature min
            attributes.push_back(0x400c); // color temperature max
        }
        else
        {
            quint16 cap = item->toNumber();
            std::vector<quint16> toCheck;
            if (cap & 0x0002) // enhanced hue supported
            {
                toCheck.push_back(0x4001); // enhanced color mode
                toCheck.push_back(0x4000); // enhanced hue
                toCheck.push_back(0x0001); // saturation
            }
            else if (cap & 0x0001)
            {
                toCheck.push_back(0x0000); // hue
                toCheck.push_back(0x0001); // saturation
                toCheck.push_back(0x0008); // color mode
            }
            else
            {
                toCheck.push_back(0x0008); // color mode
            }

            if (cap & 0x0004)
            {
                toCheck.push_back(0x4002); // Color loop active
            }

            if (cap & 0x0008)
            {
                toCheck.push_back(0x0003); // currentX
                toCheck.push_back(0x0004); // currentY
            }

            if (cap & 0x0010)
            {
                toCheck.push_back(0x0007); // color temperature
            }

            for (const deCONZ::ZclCluster &cl : lightNode->haEndpoint().inClusters())
            {
                if (cl.id() != COLOR_CLUSTER_ID)
                {
                    continue;
                }

                for (const deCONZ::ZclAttribute &attr : cl.attributes())
                {
                    for (quint16 attrId : toCheck)
                    {
                        // discard attributes which are not be available
                        if (attrId == attr.id() && attr.isAvailable())
                        {
                            NodeValue &val = restNode->getZclValue(clusterId, attrId);
                            if (isOn || !val.timestamp.isValid())
                            {
                                attributes.push_back(attrId);
                            }
                        }
                    }
                }

                break;
            }
        }
    }
    else if (suffix == RStatePresence)
    {
        clusterId = OCCUPANCY_SENSING_CLUSTER_ID;
        attributes.push_back(0x0000); // Occupancy
        attributes.push_back(0x0010); // PIR Occupied To Unoccupied Delay
    }
    else if (suffix == RStateLightLevel)
    {
        clusterId = ILLUMINANCE_MEASUREMENT_CLUSTER_ID;
        attributes.push_back(0x0000); // Measured Value
    }
    else if (suffix == RStateConsumption)
    {
        clusterId = METERING_CLUSTER_ID;
        attributes.push_back(0x0000); // Curent Summation Delivered
        attributes.push_back(0x0400); // Instantaneous Demand
    }
    else if (suffix == RStatePower)
    {
        clusterId = ELECTRICAL_MEASUREMENT_CLUSTER_ID;
        attributes.push_back(0x050b); // Active Power
        item = r->item(RAttrModelId);
        if (! item->toString().startsWith(QLatin1String("Plug"))) // OSRAM plug
        {
            attributes.push_back(0x0505); // RMS Voltage
            attributes.push_back(0x0508); // RMS Current
        }
    }
    else if (suffix == RAttrModelId)
    {
        item = r->item(RAttrModelId);
        if (item && (item->toString().isEmpty() || item->toString() == QLatin1String("unknown") ||
             (item->lastSet().secsTo(now) > READ_MODEL_ID_INTERVAL && item->toString().startsWith("FLS-A")) // dynamic model ids
            ))
        {
            clusterId = BASIC_CLUSTER_ID;
            //attributes.push_back(0x0004); // manufacturer
            attributes.push_back(0x0005); // model id
        }
    }
    else if (suffix == RAttrSwVersion && lightNode)
    {
        item = r->item(RAttrSwVersion);
        if (item && (item->toString().isEmpty() ||
             (item->lastSet().secsTo(now) > READ_SWBUILD_ID_INTERVAL))) // dynamic
        {

            if (lightNode->manufacturerCode() == VENDOR_UBISYS ||
                lightNode->manufacturerCode() == VENDOR_EMBER ||
                lightNode->manufacturerCode() == VENDOR_120B ||
                lightNode->manufacturerCode() == VENDOR_115F ||
                lightNode->manufacturer().startsWith(QLatin1String("Climax")))
            {
                if (item->toString().isEmpty())
                {
                    attributes.push_back(0x0006); // date code
                    clusterId = BASIC_CLUSTER_ID;
                }
            }
            else
            {
                if (item->toString().isEmpty() ||
                    lightNode->manufacturerCode() == VENDOR_XAL ||
                    lightNode->manufacturerCode() == VENDOR_DDEL)
                {
                    attributes.push_back(0x4000); // sw build id
                    clusterId = BASIC_CLUSTER_ID;
                }
            }
        }
    }

    size_t fresh = 0;
    const int reportWaitTime = 360;
    for (quint16 attrId : attributes)
    {
        // force polling after node becomes reachable, since reporting might not be active
        if (dtReachable < reportWaitTime)
        {
            break;
        }

        NodeValue &val = restNode->getZclValue(clusterId, attrId);

        if (val.timestampLastReport.isValid() && val.timestampLastReport.secsTo(now) < reportWaitTime)
        {
            fresh++;
        }
    }

    // check that cluster exists on endpoint
    if (clusterId != 0xffff)
    {
        bool found = false;
        deCONZ::SimpleDescriptor sd;
        if (restNode->node()->copySimpleDescriptor(pitem.endpoint, &sd) == 0)
        {
            for (const auto &cl : sd.inClusters())
            {
                if (cl.id() == clusterId)
                {
                    found = true;
                    break;
                }
            }
        }

        if (!found)
        {
            DBG_Printf(DBG_INFO, "Poll APS request to 0x%016llX cluster: 0x%04X dropped, cluster doesn't exist\n", pitem.address.ext(), clusterId);
            clusterId = 0xffff;
        }
    }

    if (clusterId != 0xffff && fresh > 0 && fresh == attributes.size())
    {
        DBG_Printf(DBG_INFO, "Poll APS request to 0x%016llX cluster: 0x%04X dropped, values are fresh enough\n", pitem.address.ext(), clusterId);
        suffix = nullptr; // clear
        timer->start(100);
    }
    else if (!attributes.empty() && clusterId != 0xffff &&
        plugin->readAttributes(restNode, pitem.endpoint, clusterId, attributes))
    {
        pollState = StateWait;
        // TODO this hack to get aps request id
        DBG_Assert(plugin->tasks.back().taskType == TaskReadAttributes);
        apsReqId = plugin->tasks.back().req.id();
        dstAddr = pitem.address;
        timer->start(60 * 1000); // wait for confirm
        suffix = nullptr; // clear
        DBG_Printf(DBG_INFO_L2, "Poll APS request %u to 0x%016llX cluster: 0x%04X\n", apsReqId, dstAddr.ext(), clusterId);
    }
    else if (suffix)
    {
        suffix = nullptr; // clear
        timer->start(100);
    }
    else
    {
        if (clusterId != 0xffff)
        {
            DBG_Printf(DBG_INFO, "Poll APS request to 0x%016llX cluster: 0x%04X dropped\n", pitem.address.ext(), clusterId);
        }
        timer->start(100);
        items.front() = items.back();
        items.pop_back();
    }
}
<commit_msg>Query color cluster of ZHA lights like OSRAM LEDVANCE (US)<commit_after>/*
 * Copyright (c) 2017-2018 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include "poll_manager.h"
#include "de_web_plugin_private.h"

/*! Constructor.
 */
PollManager::PollManager(QObject *parent) :
    QObject(parent)
{
    pollState = StateIdle;
    timer = new QTimer(this);
    timer->setSingleShot(true);
    connect(timer, SIGNAL(timeout()), this, SLOT(pollTimerFired()));
    plugin = qobject_cast<DeRestPluginPrivate*>(parent);
}

/*! Queues polling of the node.
    \param restNode - the node to poll
 */
void PollManager::poll(RestNodeBase *restNode, const QDateTime &tStart)
{
    Resource *r = dynamic_cast<Resource*>(restNode);
    DBG_Assert(r);
    if (!r || !restNode->node())
    {
        return;
    }

    PollItem pitem;

    if (!restNode->node()->nodeDescriptor().receiverOnWhenIdle())
    {
        return;
    }

    LightNode *lightNode = nullptr;
    Sensor *sensor = nullptr;

    if (r->prefix() == RLights)
    {
        lightNode = dynamic_cast<LightNode*>(restNode);
        DBG_Assert(lightNode);
        pitem.endpoint = lightNode->haEndpoint().endpoint();
        DBG_Printf(DBG_INFO_L2, ">>>> Poll light node %s\n", qPrintable(lightNode->name()));
    }
    else if (r->prefix() == RSensors)
    {
        sensor = dynamic_cast<Sensor*>(restNode);
        DBG_Assert(sensor);
        pitem.endpoint = sensor->fingerPrint().endpoint;
        DBG_Printf(DBG_INFO_L2, ">>>> Poll %s sensor node %s\n", qPrintable(sensor->type()), qPrintable(sensor->name()));
    }
    else
    {
        return;
    }

    pitem.id = restNode->id();
    pitem.prefix = r->prefix();
    pitem.address = restNode->address();
    pitem.tStart = tStart;

    for (int i = 0; i < r->itemCount(); i++)
    {
        const ResourceItem *item = r->itemForIndex(i);
        const char *suffix = item ? item->descriptor().suffix : nullptr;

        if (suffix == RStateOn ||
            suffix == RStateBri ||
            suffix == RStateColorMode ||
            (suffix == RStateConsumption && sensor && sensor->type() == QLatin1String("ZHAConsumption")) ||
            (suffix == RStatePower && sensor && sensor->type() == QLatin1String("ZHAPower")) ||
            (suffix == RStatePresence && sensor && sensor->type() == QLatin1String("ZHAPresence")) ||
            (suffix == RStateLightLevel && sensor && sensor->type() == QLatin1String("ZHALightLevel")) ||
            suffix == RAttrModelId ||
            suffix == RAttrSwVersion)
        {
            // DBG_Printf(DBG_INFO_L2, "    attribute %s\n", suffix);
            pitem.items.push_back(suffix);
        }
    }

    for (PollItem &i : items)
    {
        if (i.prefix == r->prefix() && i.id == restNode->id())
        {
            i.items = pitem.items; // update
            if (tStart.isValid())
            {
                i.tStart = tStart;
            }
            return;
        }
    }

    items.push_back(pitem);

    if (!timer->isActive())
    {
        timer->start(1);
    }
}

/*! Delays polling for \p ms milliseconds.
 */
void PollManager::delay(int ms)
{
    timer->stop();
    timer->start(ms);
}

/*! Handle APS confirm if related to polling.
 */
void PollManager::apsdeDataConfirm(const deCONZ::ApsDataConfirm &conf)
{
    if (pollState != StateWait)
    {
        return;
    }

    if (apsReqId != conf.id())
    {
        return;
    }

    if (dstAddr.hasExt() && conf.dstAddress().hasExt()
        && dstAddr.ext() != conf.dstAddress().ext())
    {

    }

    else if (dstAddr.hasNwk() && conf.dstAddress().hasNwk()
        && dstAddr.nwk() != conf.dstAddress().nwk())
    {

    }

    DBG_Printf(DBG_INFO_L2, "Poll APS confirm %u status: 0x%02X\n", conf.id(), conf.status());

    pollState = StateIdle;
    timer->stop();
    timer->start(1);
}

/*! Timer callback to proceed polling.
 */
void PollManager::pollTimerFired()
{
    if (pollState == StateWait)
    {
        DBG_Printf(DBG_INFO, "timeout on poll APS confirm\n");
        pollState = StateIdle;
    }

    DBG_Assert(pollState == StateIdle);

    if (items.empty())
    {
        return;
    }

    QDateTime now = QDateTime::currentDateTime();
    PollItem &pitem = items.front();
    Resource *r = plugin->getResource(pitem.prefix, pitem.id);
    ResourceItem *item = nullptr;
    RestNodeBase *restNode = nullptr;
    const LightNode *lightNode = nullptr;
    if (r && r->prefix() == RLights)
    {
        restNode = plugin->getLightNodeForId(pitem.id);
        lightNode = dynamic_cast<LightNode*>(restNode);
        item = r->item(RStateReachable);
    }
    else if (r && r->prefix() == RSensors)
    {
        restNode = plugin->getSensorNodeForId(pitem.id);
        item = r->item(RConfigReachable);
    }

    if (pitem.tStart.isValid() && pitem.tStart > now)
    {
        if (items.size() > 1)
        {
            PollItem tmp = pitem;
            items.front() = items.back();
            items.back() = tmp;
        }
        timer->start(1);
        return;
    }

    if (!r || pitem.items.empty() ||
        !restNode ||
        //!restNode->lastRx().isValid() ||
        !item || !item->toBool()) // not reachable
    {
        items.front() = items.back();
        items.pop_back();
        timer->start(1);
        return;
    }

    const auto dtReachable = item->lastSet().secsTo(now);

    quint16 clusterId = 0xffff; // invalid
    std::vector<quint16> attributes;

    item = r->item(RStateOn);
    bool isOn = item ? item->toBool() : false;
    const char *&suffix = pitem.items[0];

    for (size_t i = 0; pitem.items[0] == nullptr && i < pitem.items.size(); i++)
    {
        if (pitem.items[i] != nullptr)
        {
            pitem.items[0] = pitem.items[i]; // move to front
            pitem.items[i] = nullptr; // clear
            break;
        }
    }

    if (!suffix)
    {
        pitem.items.clear(); // all done
    }

    if (suffix == RStateOn)
    {
        if (lightNode && lightNode->manufacturerCode() != VENDOR_115F) // reports
        {
            clusterId = ONOFF_CLUSTER_ID;
            attributes.push_back(0x0000); // onOff
        }
    }
    else if (suffix == RStateBri && isOn)
    {
        NodeValue &val = restNode->getZclValue(LEVEL_CLUSTER_ID, 0x0000);

        if (isOn || !val.timestamp.isValid())
        {
            clusterId = LEVEL_CLUSTER_ID;
            attributes.push_back(0x0000); // current level
        }
    }
    else if (suffix == RStateColorMode && lightNode)
    {
        clusterId = COLOR_CLUSTER_ID;
        item = r->item(RConfigColorCapabilities);

        if ((!item || item->toNumber() <= 0) && lightNode->haEndpoint().profileId() != HA_PROFILE_ID)
        {
            attributes.push_back(0x0008); // color mode
            attributes.push_back(0x4001); // enhanced color mode
            attributes.push_back(0x400a); // color capabilities
            attributes.push_back(0x400b); // color temperature min
            attributes.push_back(0x400c); // color temperature max
        }
        else
        {
            quint16 cap = item ? static_cast<quint16>(item->toNumber()) : 0;

            if (cap == 0 && lightNode->haEndpoint().profileId() == HA_PROFILE_ID)
            {
                // e.g. OSRAM US version
                // DEV_ID_HA_COLOR_DIMMABLE_LIGHT
                cap  = (0x0001 | 0x0008); // hue, saturation, color mode, xy
            }

            std::vector<quint16> toCheck;
            if (cap & 0x0002) // enhanced hue supported
            {
                toCheck.push_back(0x4001); // enhanced color mode
                toCheck.push_back(0x4000); // enhanced hue
                toCheck.push_back(0x0001); // saturation
            }
            else if (cap & 0x0001)
            {
                toCheck.push_back(0x0000); // hue
                toCheck.push_back(0x0001); // saturation
                toCheck.push_back(0x0008); // color mode
            }
            else
            {
                toCheck.push_back(0x0008); // color mode
            }

            if (cap & 0x0004)
            {
                toCheck.push_back(0x4002); // Color loop active
            }

            if (cap & 0x0008)
            {
                toCheck.push_back(0x0003); // currentX
                toCheck.push_back(0x0004); // currentY
            }

            if (cap & 0x0010)
            {
                toCheck.push_back(0x0007); // color temperature
            }

            for (const deCONZ::ZclCluster &cl : lightNode->haEndpoint().inClusters())
            {
                if (cl.id() != COLOR_CLUSTER_ID)
                {
                    continue;
                }

                for (const deCONZ::ZclAttribute &attr : cl.attributes())
                {
                    for (quint16 attrId : toCheck)
                    {
                        // discard attributes which are not be available
                        if (attrId == attr.id() && attr.isAvailable())
                        {
                            NodeValue &val = restNode->getZclValue(clusterId, attrId);
                            if (isOn || !val.timestamp.isValid())
                            {
                                attributes.push_back(attrId);
                            }
                        }
                    }
                }

                break;
            }
        }
    }
    else if (suffix == RStatePresence)
    {
        clusterId = OCCUPANCY_SENSING_CLUSTER_ID;
        attributes.push_back(0x0000); // Occupancy
        attributes.push_back(0x0010); // PIR Occupied To Unoccupied Delay
    }
    else if (suffix == RStateLightLevel)
    {
        clusterId = ILLUMINANCE_MEASUREMENT_CLUSTER_ID;
        attributes.push_back(0x0000); // Measured Value
    }
    else if (suffix == RStateConsumption)
    {
        clusterId = METERING_CLUSTER_ID;
        attributes.push_back(0x0000); // Curent Summation Delivered
        attributes.push_back(0x0400); // Instantaneous Demand
    }
    else if (suffix == RStatePower)
    {
        clusterId = ELECTRICAL_MEASUREMENT_CLUSTER_ID;
        attributes.push_back(0x050b); // Active Power
        item = r->item(RAttrModelId);
        if (! item->toString().startsWith(QLatin1String("Plug"))) // OSRAM plug
        {
            attributes.push_back(0x0505); // RMS Voltage
            attributes.push_back(0x0508); // RMS Current
        }
    }
    else if (suffix == RAttrModelId)
    {
        item = r->item(RAttrModelId);
        if (item && (item->toString().isEmpty() || item->toString() == QLatin1String("unknown") ||
             (item->lastSet().secsTo(now) > READ_MODEL_ID_INTERVAL && item->toString().startsWith("FLS-A")) // dynamic model ids
            ))
        {
            clusterId = BASIC_CLUSTER_ID;
            //attributes.push_back(0x0004); // manufacturer
            attributes.push_back(0x0005); // model id
        }
    }
    else if (suffix == RAttrSwVersion && lightNode)
    {
        item = r->item(RAttrSwVersion);
        if (item && (item->toString().isEmpty() ||
             (item->lastSet().secsTo(now) > READ_SWBUILD_ID_INTERVAL))) // dynamic
        {

            if (lightNode->manufacturerCode() == VENDOR_UBISYS ||
                lightNode->manufacturerCode() == VENDOR_EMBER ||
                lightNode->manufacturerCode() == VENDOR_120B ||
                lightNode->manufacturerCode() == VENDOR_115F ||
                lightNode->manufacturer().startsWith(QLatin1String("Climax")))
            {
                if (item->toString().isEmpty())
                {
                    attributes.push_back(0x0006); // date code
                    clusterId = BASIC_CLUSTER_ID;
                }
            }
            else
            {
                if (item->toString().isEmpty() ||
                    lightNode->manufacturerCode() == VENDOR_XAL ||
                    lightNode->manufacturerCode() == VENDOR_DDEL)
                {
                    attributes.push_back(0x4000); // sw build id
                    clusterId = BASIC_CLUSTER_ID;
                }
            }
        }
    }

    size_t fresh = 0;
    const int reportWaitTime = 360;
    for (quint16 attrId : attributes)
    {
        // force polling after node becomes reachable, since reporting might not be active
        if (dtReachable < reportWaitTime)
        {
            break;
        }

        NodeValue &val = restNode->getZclValue(clusterId, attrId);

        if (val.timestampLastReport.isValid() && val.timestampLastReport.secsTo(now) < reportWaitTime)
        {
            fresh++;
        }
    }

    // check that cluster exists on endpoint
    if (clusterId != 0xffff)
    {
        bool found = false;
        deCONZ::SimpleDescriptor sd;
        if (restNode->node()->copySimpleDescriptor(pitem.endpoint, &sd) == 0)
        {
            for (const auto &cl : sd.inClusters())
            {
                if (cl.id() == clusterId)
                {
                    found = true;
                    break;
                }
            }
        }

        if (!found)
        {
            DBG_Printf(DBG_INFO, "Poll APS request to 0x%016llX cluster: 0x%04X dropped, cluster doesn't exist\n", pitem.address.ext(), clusterId);
            clusterId = 0xffff;
        }
    }

    if (clusterId != 0xffff && fresh > 0 && fresh == attributes.size())
    {
        DBG_Printf(DBG_INFO, "Poll APS request to 0x%016llX cluster: 0x%04X dropped, values are fresh enough\n", pitem.address.ext(), clusterId);
        suffix = nullptr; // clear
        timer->start(100);
    }
    else if (!attributes.empty() && clusterId != 0xffff &&
        plugin->readAttributes(restNode, pitem.endpoint, clusterId, attributes))
    {
        pollState = StateWait;
        // TODO this hack to get aps request id
        DBG_Assert(plugin->tasks.back().taskType == TaskReadAttributes);
        apsReqId = plugin->tasks.back().req.id();
        dstAddr = pitem.address;
        timer->start(60 * 1000); // wait for confirm
        suffix = nullptr; // clear
        DBG_Printf(DBG_INFO_L2, "Poll APS request %u to 0x%016llX cluster: 0x%04X\n", apsReqId, dstAddr.ext(), clusterId);
    }
    else if (suffix)
    {
        suffix = nullptr; // clear
        timer->start(100);
    }
    else
    {
        if (clusterId != 0xffff)
        {
            DBG_Printf(DBG_INFO, "Poll APS request to 0x%016llX cluster: 0x%04X dropped\n", pitem.address.ext(), clusterId);
        }
        timer->start(100);
        items.front() = items.back();
        items.pop_back();
    }
}
<|endoftext|>