<commit_before>use crate::utils::ptr::get_spans;
use crate::utils::{
    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
    snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
use rustc_typeck::expr_use_visitor as euv;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** Checks for functions taking arguments by value, but not
    /// consuming them in its
    /// body.
    ///
    /// **Why is this bad?** Taking arguments by reference is more flexible and can
    /// sometimes avoid
    /// unnecessary allocations.
    ///
    /// **Known problems:**
    /// * This lint suggests taking an argument by reference,
    /// however sometimes it is better to let users decide the argument type
    /// (by using `Borrow` trait, for example), depending on how the function is used.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(v: Vec<i32>) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    /// should be
    /// ```rust
    /// fn foo(v: &[i32]) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    pub NEEDLESS_PASS_BY_VALUE,
    pedantic,
    "functions taking arguments by value, but not consuming them in its body"
}

declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);

macro_rules! need {
    ($e: expr) => {
        if let Some(x) = $e {
            x
        } else {
            return;
        }
    };
}

impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if span.from_expansion() {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                    return;
                }
            },
            FnKind::Method(..) => (),
            FnKind::Closure(..) => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
            if matches!(
                item.kind,
                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
            ) {
                return;
            }
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let allowed_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
            .filter(|p| !p.is_global())
            .filter_map(|obligation| {
                // Note that we do not want to deal with qualified predicates here.
                let binder = obligation.predicate.kind();
                match binder.skip_binder() {
                    ty::PredicateAtom::Trait(pred, _) if !binder.has_escaping_bound_vars() => {
                        if pred.def_id() == sized_trait {
                            return None;
                        }
                        Some(pred)
                    }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::default();
            cx.tcx.infer_ctxt().enter(|infcx| {
                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
                    .consume_body(body);
            });
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
            // All spans generated from a proc-macro invocation are the same...
            if span == input.span {
                return;
            }

            // Ignore `self`s.
            if idx == 0 {
                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
                    if ident.as_str() == "self" {
                        continue;
                    }
                }
            }

            //
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
            //   `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && {
                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                        preds.iter().all(|t| {
                            let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                            implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
                        })
                    },
                )
            };

            if_chain! {
                if !is_self(arg);
                if !ty.is_mutable_ptr();
                if !is_copy(cx, ty);
                if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
                if !implements_borrow_trait;
                if !all_borrowable_trait;

                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
                if !moved_vars.contains(&canonical_id);
                then {
                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                        continue;
                    }

                    // Dereference suggestion
                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
                        if let ty::Adt(def, ..) = ty.kind() {
                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
                                    diag.span_help(span, "consider marking this type as `Copy`");
                                }
                            }
                        }

                        let deref_span = spans_need_deref.get(&canonical_id);
                        if_chain! {
                            if is_type_diagnostic_item(cx, ty, sym::vec_type);
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
                            if let Some(elem_ty) = path.segments.iter()
                                .find(|seg| seg.ident.name == sym::Vec)
                                .and_then(|ps| ps.args.as_ref())
                                .map(|params| params.args.iter().find_map(|arg| match arg {
                                    GenericArg::Type(ty) => Some(ty),
                                    _ => None,
                                }).unwrap());
                            then {
                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    slice_ty,
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x)),
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                // cannot be destructured, no need for `*` suggestion
                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        if is_type_diagnostic_item(cx, ty, sym::string_type) {
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    "&str".to_string(),
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x))
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                        // Suggests adding `*` to dereference the added reference.
                        if let Some(deref_span) = deref_span {
                            spans.extend(
                                deref_span
                                    .iter()
                                    .cloned()
                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                            );
                            spans.sort_by_key(|&(span, _)| span);
                        }
                        multispan_sugg(diag, "consider taking a reference instead", spans);
                    };

                    span_lint_and_then(
                        cx,
                        NEEDLESS_PASS_BY_VALUE,
                        input.span,
                        "this argument is passed by value, but not consumed in the function body",
                        sugg,
                    );
                }
            }
        }
    }
}

/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
            .iter()
            .any(|&allow| attr.has_name(allow))
    })
}

#[derive(Default)]
struct MovedVariablesCtxt {
    moved_vars: FxHashSet<HirId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
}

impl MovedVariablesCtxt {
    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
        if let euv::PlaceBase::Local(vid) = cmt.place.base {
            self.moved_vars.insert(vid);
        }
    }
}

impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move = mode {
            self.move_common(cmt);
        }
    }

    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}

    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
}
<commit_msg>Cleanup<commit_after>use crate::utils::ptr::get_spans;
use crate::utils::{
    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
    snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
use rustc_typeck::expr_use_visitor as euv;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** Checks for functions taking arguments by value, but not
    /// consuming them in its
    /// body.
    ///
    /// **Why is this bad?** Taking arguments by reference is more flexible and can
    /// sometimes avoid
    /// unnecessary allocations.
    ///
    /// **Known problems:**
    /// * This lint suggests taking an argument by reference,
    /// however sometimes it is better to let users decide the argument type
    /// (by using `Borrow` trait, for example), depending on how the function is used.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(v: Vec<i32>) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    /// should be
    /// ```rust
    /// fn foo(v: &[i32]) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    pub NEEDLESS_PASS_BY_VALUE,
    pedantic,
    "functions taking arguments by value, but not consuming them in its body"
}

declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);

macro_rules! need {
    ($e: expr) => {
        if let Some(x) = $e {
            x
        } else {
            return;
        }
    };
}

impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if span.from_expansion() {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                    return;
                }
            },
            FnKind::Method(..) => (),
            FnKind::Closure(..) => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
            if matches!(
                item.kind,
                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
            ) {
                return;
            }
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let allowed_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
            .filter(|p| !p.is_global())
            .filter_map(|obligation| {
                // Note that we do not want to deal with qualified predicates here.
                let binder = obligation.predicate.bound_atom();
                match binder.skip_binder() {
                    ty::PredicateAtom::Trait(pred, _) if !binder.has_escaping_bound_vars() => {
                        if pred.def_id() == sized_trait {
                            return None;
                        }
                        Some(pred)
                    }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::default();
            cx.tcx.infer_ctxt().enter(|infcx| {
                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
                    .consume_body(body);
            });
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
            // All spans generated from a proc-macro invocation are the same...
            if span == input.span {
                return;
            }

            // Ignore `self`s.
            if idx == 0 {
                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
                    if ident.as_str() == "self" {
                        continue;
                    }
                }
            }

            //
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
            //   `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && {
                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                        preds.iter().all(|t| {
                            let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                            implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
                        })
                    },
                )
            };

            if_chain! {
                if !is_self(arg);
                if !ty.is_mutable_ptr();
                if !is_copy(cx, ty);
                if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
                if !implements_borrow_trait;
                if !all_borrowable_trait;

                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
                if !moved_vars.contains(&canonical_id);
                then {
                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                        continue;
                    }

                    // Dereference suggestion
                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
                        if let ty::Adt(def, ..) = ty.kind() {
                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
                                    diag.span_help(span, "consider marking this type as `Copy`");
                                }
                            }
                        }

                        let deref_span = spans_need_deref.get(&canonical_id);
                        if_chain! {
                            if is_type_diagnostic_item(cx, ty, sym::vec_type);
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
                            if let Some(elem_ty) = path.segments.iter()
                                .find(|seg| seg.ident.name == sym::Vec)
                                .and_then(|ps| ps.args.as_ref())
                                .map(|params| params.args.iter().find_map(|arg| match arg {
                                    GenericArg::Type(ty) => Some(ty),
                                    _ => None,
                                }).unwrap());
                            then {
                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    slice_ty,
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x)),
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                // cannot be destructured, no need for `*` suggestion
                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        if is_type_diagnostic_item(cx, ty, sym::string_type) {
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    "&str".to_string(),
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x))
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                        // Suggests adding `*` to dereference the added reference.
                        if let Some(deref_span) = deref_span {
                            spans.extend(
                                deref_span
                                    .iter()
                                    .cloned()
                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                            );
                            spans.sort_by_key(|&(span, _)| span);
                        }
                        multispan_sugg(diag, "consider taking a reference instead", spans);
                    };

                    span_lint_and_then(
                        cx,
                        NEEDLESS_PASS_BY_VALUE,
                        input.span,
                        "this argument is passed by value, but not consumed in the function body",
                        sugg,
                    );
                }
            }
        }
    }
}

/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
            .iter()
            .any(|&allow| attr.has_name(allow))
    })
}

#[derive(Default)]
struct MovedVariablesCtxt {
    moved_vars: FxHashSet<HirId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
}

impl MovedVariablesCtxt {
    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
        if let euv::PlaceBase::Local(vid) = cmt.place.base {
            self.moved_vars.insert(vid);
        }
    }
}

impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move = mode {
            self.move_common(cmt);
        }
    }

    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}

    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
}
<|endoftext|>


<commit_before>//! trained NN to represent value heuristic and use minimax
#![allow(dead_code)]

extern crate rand;
extern crate nn;
//extern crate rustc_serialize;

//use self::rustc_serialize::json;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::io::prelude::*;
use self::rand::Rng;
use self::nn::{NN, HaltCondition, Activation};
use super::Player;
use super::super::field::Field;
use std::f64;

const DEEPNESS:u32 = 3; //recursion limit
const LEARN_FREQ:u32 = 10; //number of games between learning to collect data to train with
const GAMMA:f64 = 0.9; //temporal unsureness factor
const LR:f64 = 0.02; //neural net learning rate (deterministic -> high)
const LR_DECAY:f64 = 0.01 / 5000f64; //NN learning rate decrease per game(s)
const LR_MIN:f64 = 0.005; //minimum NN LR
const LAMBDA:f64 = 0.0001; //L2 regularization parameter lambda (divide by n manually, pick very small > 0, like pick LAMBDA / n)
const MOM:f64 = 0.5; //neural net momentum
const EPOCHS:u32 = 2; //NN training epochs for per data set

//values for a won or lost game in minimax and heuristic (neural net outputs should be closer to zero)
const VAL_MAX:f64 = 2.0; //f64::MAX
const VAL_MIN:f64 = -2.0; //f64::MIN
//values for value to train with NN
const VAL_WIN:f64 = 0.9; //starting player wins value for NN learning
const VAL_DRAW:f64 = 0.0; //draw's value for NN learning
const VAL_LOSE:f64 = -0.9; //starting player loses value for NN learning

pub struct PlayerAIValue
{
	initialized: bool,
	fixed: bool, //fixed agent? (don't learn)
	pid: i32, //player ID
	startp: i32, //starting player
	games_played: u32, //number of games the agent played
	filename: String, //file name for NN/agent information
	nn: Option<NN>, //neural network for neutral state evaluation (value based on starting player)
	lr: f64, //NN learning rate
	current_game: Vec<Vec<f64>>, //buffer for states, that occured in the current game
	games_buffer: Vec<(Vec<f64>,Vec<f64>)>, //buffer of game data to learn -> training buffer
}

impl PlayerAIValue
{
	pub fn new(fix: bool) -> Box<PlayerAIValue>
	{
		Box::new(PlayerAIValue { initialized: false, fixed: fix, pid: 0, startp: 0, games_played: 0,
						filename: String::new(), nn: None, lr: LR, current_game: Vec::new(),
						games_buffer: Vec::new() })
	}

	fn get_lr(&self) -> f64
	{
		LR_MIN.max(LR - LR_DECAY * self.games_played as f64)
	}

	//raw field
	fn field_to_input(field:&mut Field, p:i32) -> Vec<f64>
	{ //input: p = start player
		let op:i32 = if p == 1 { 2 } else { 1 }; //other player
		let mut input:Vec<f64> = Vec::with_capacity(field.get_size() as usize);
		//1 nodes for every square: -1 enemy, 0 free, 1 own
		for val in field.get_field().iter()
		{
			if *val == p { input.push(1f64); }
			else if *val == op { input.push(-1f64); }
			else { input.push(0f64); } //empty square
		}
		//return
		input
	}

	//returns value of board position: +1.0 player wins, -1.0 other player wins, 0.0 draw or even board
	fn heur(&self, field:&mut Field, p:i32) -> f64 //p = player. translated from start player by (value * -1) if they are not same.
	{
		let op = if p == 1 {2} else {1};
		let state = field.get_state(); //return best or worst value on win/loose (neutral on tie)
		if state == -1 { return 0.0; }
		else if state == p { return VAL_MAX; }
		else if state == op { return VAL_MIN; }
		else
		{ //game running -> evaluate
			let nn = self.nn.as_ref().unwrap();
			let state = PlayerAIValue::field_to_input(field, self.startp);
			let factor = if p == self.startp { 1.0 } else { -1.0 }; //if p is not startp value has to be reversed

			let result = nn.run(&state);
			let value = factor * result[0];

			return value;
		}
	}

	fn minimax(&self, field:&mut Field, p:i32, deep:u32) -> f64
	{
		let op = if p == 1 {2} else {1};
		if deep > DEEPNESS { return self.heur(field, if deep%2 == 0 {op} else {p}); } //leaf node -> return evaluated heuristic, mechanism to get heur always for same player
		let state = field.get_state(); //return early on game end
		if state == -1 { return 0.0; }
		else if state == p { return if deep%2 == 0 {VAL_MIN} else {VAL_MAX}; }
		else if state == op { return if deep%2 == 0 {VAL_MAX} else {VAL_MIN}; }

		//else: game running -> go deeper
		let mut heur = if deep%2 == 0 { f64::INFINITY } else { f64::NEG_INFINITY };
		for i in 0..field.get_w()
		{
			if field.play(p, i)
			{
				let val = self.minimax(field, op, deep+1);
				field.undo();
				if (deep%2 == 0 && val < heur) || (deep%2 == 1 && val > heur) //min or max according to which player's turn it is
				{
					heur = val;
				}
			}
		}
		heur
	}

	fn learn_from_data(&mut self)
	{
		//use the collected data to improve the neural net
		let nn = self.nn.as_mut().unwrap();
		let mut rng = rand::thread_rng();

		//shuffle data
		let len = self.games_buffer.len();
		for _ in 0..len
		{ //n random O(1) operations on the buffer to shuffle
			let i = rng.gen::<usize>() % len;
			let item = self.games_buffer.swap_remove(i);
			self.games_buffer.push(item);
		}

		//learn
		nn.train(&self.games_buffer)
			.halt_condition(HaltCondition::Epochs(EPOCHS))
			.log_interval(None)
			.momentum(MOM)
			.rate(self.lr)
			.lambda(LAMBDA / (self.games_played as f64 + 1000.0))
			.go();

		//flush buffer
		self.games_buffer.clear();
	}
}

impl Player for PlayerAIValue
{
	#[allow(unused_variables)]
	fn init(&mut self, field:&Field, p:i32) -> bool
	{
		if DEEPNESS < 1 { return false; } //invalid player, could cause bugs else

		self.pid = p;

		self.filename = format!("AIValue-{}x{}.NN", field.get_w(), field.get_h());
		let file = File::open(&self.filename);
		if file.is_err()
		{
			//create new neural net, as it could not be loaded
			let n = field.get_size();
			self.nn = Some(NN::new(&[n, 6*n, 3*n, n, 1], Activation::PELU, Activation::Tanh)); //set size of NN layers here, be careful with activation function
			//games_played, lr already set
		}
		else
		{
			//load neural net from file (and games played)
			let mut reader = BufReader::new(file.unwrap());
			let mut datas = String::new();
			let mut nns = String::new();

			let res1 = reader.read_line(&mut datas);
			let res2 = reader.read_line(&mut nns);
			if res1.is_err() || res2.is_err() { return false; }

			let res = datas.trim().parse::<u32>();
			if res.is_err() { return false; }
			self.games_played = res.unwrap();
			self.nn = Some(NN::from_json(&nns));

			self.lr = self.get_lr();
		}

		self.initialized = true;
		true
	}

	#[allow(unused_variables)]
	fn startp(&mut self, p:i32)
	{
		self.startp = p;
	}

	fn play(&mut self, field:&mut Field) -> bool
	{
		if !self.initialized { return false; }

		let p = self.pid;
		let op = if p == 1 {2} else {1};

		//decide which action x to take
		let mut x:u32 = 0;
		let mut max = f64::NEG_INFINITY;
		//decide by evaluation
		for i in 0..field.get_w()
		{
			let mut val = f64::NEG_INFINITY;
			if field.play(p, i)
			{
				val = self.minimax(field, op, 2);
				field.undo();
			}
			if max < val || !field.is_valid_play(x)
			{
				max = val;
				x = i;
			}
		}

		//save game state for later mapping to win/loose. only if not fixed to save memory
		if !self.fixed
		{
			let state = PlayerAIValue::field_to_input(field, self.startp);
			self.current_game.push(state);
		}

		//debug
		//println!("Heur: {}", max);

		//play (actually should always be true, unless game was finished before method invocation)
		field.play(p, x)
	}

	#[allow(unused_variables)]
	fn outcome(&mut self, field:&mut Field, state:i32)
	{
		//parameters
		self.games_played += 1;
		self.lr = self.get_lr();

		//collect data and learn if not fixed (to save memory and computation else)
		if !self.fixed
		{
			//collect data
			let op:i32 = if self.startp == 1 { 2 } else { 1 }; //other player
			let mut temporal_factor = 1.0;
			let mut value = VAL_DRAW; //draw
			if state == self.startp { value = VAL_WIN; } //win
			else if state == op { value = VAL_LOSE; } //lose

			while !self.current_game.is_empty()
			{
				let state = self.current_game.pop().unwrap();
				let result = vec![temporal_factor * value];
				self.games_buffer.push((state, result));
				temporal_factor *= GAMMA; //induce unsureness at the start of the game, avoid always 1 for start player
			}

			//learn if it is time
			if self.games_played % LEARN_FREQ == 0
			{
				self.learn_from_data();
			}
		}
	}
}

impl Drop for PlayerAIValue
{
	fn drop(&mut self)
	{
		//if it was allowed to learn and was initialized:
		if self.initialized && !self.fixed
		{
			//learn from remaining data
			self.learn_from_data();

			//write neural net to file
			let file = File::create(&self.filename);
			if file.is_err() { println!("Warning: Could not write AIValue NN file!"); return; }
			let mut writer = BufWriter::new(file.unwrap());

			let res1 = writeln!(&mut writer, "{}", self.games_played);
			let res2 = writeln!(&mut writer, "{}", self.nn.as_mut().unwrap().to_json());
			if res1.is_err() || res2.is_err() { println!("Warning: There was an error while writing AIValue NN file!"); return; }
		}
	}
}
<commit_msg>comment clarification<commit_after>//! trained NN to represent value heuristic and use minimax
#![allow(dead_code)]

extern crate rand;
extern crate nn;
//extern crate rustc_serialize;

//use self::rustc_serialize::json;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::io::prelude::*;
use self::rand::Rng;
use self::nn::{NN, HaltCondition, Activation};
use super::Player;
use super::super::field::Field;
use std::f64;

const DEEPNESS:u32 = 3; //recursion limit
const LEARN_FREQ:u32 = 10; //number of games between learning to collect data to train with
const GAMMA:f64 = 0.9; //temporal unsureness factor
const LR:f64 = 0.02; //neural net learning rate (deterministic -> high)
const LR_DECAY:f64 = 0.01 / 5000f64; //NN learning rate decrease per game(s)
const LR_MIN:f64 = 0.005; //minimum NN LR
const LAMBDA:f64 = 0.0001; //L2 regularization parameter lambda (divide by n manually, pick very small > 0, like pick LAMBDA / n)
const MOM:f64 = 0.5; //neural net momentum
const EPOCHS:u32 = 2; //NN training epochs for per data set

//values for a won or lost game in minimax and heuristic (neural net outputs should be closer to zero)
const VAL_MAX:f64 = 2.0; //f64::MAX
const VAL_MIN:f64 = -2.0; //f64::MIN
//values for value to train with NN
const VAL_WIN:f64 = 0.9; //starting player wins value for NN learning (greater than 0!)
const VAL_DRAW:f64 = 0.0; //draw's value for NN learning (0!)
const VAL_LOSE:f64 = -0.9; //starting player loses value for NN learning (less than 0!)

pub struct PlayerAIValue
{
	initialized: bool,
	fixed: bool, //fixed agent? (don't learn)
	pid: i32, //player ID
	startp: i32, //starting player
	games_played: u32, //number of games the agent played
	filename: String, //file name for NN/agent information
	nn: Option<NN>, //neural network for neutral state evaluation (value based on starting player)
	lr: f64, //NN learning rate
	current_game: Vec<Vec<f64>>, //buffer for states, that occured in the current game
	games_buffer: Vec<(Vec<f64>,Vec<f64>)>, //buffer of game data to learn -> training buffer
}

impl PlayerAIValue
{
	pub fn new(fix: bool) -> Box<PlayerAIValue>
	{
		Box::new(PlayerAIValue { initialized: false, fixed: fix, pid: 0, startp: 0, games_played: 0,
						filename: String::new(), nn: None, lr: LR, current_game: Vec::new(),
						games_buffer: Vec::new() })
	}

	fn get_lr(&self) -> f64
	{
		LR_MIN.max(LR - LR_DECAY * self.games_played as f64)
	}

	//raw field
	fn field_to_input(field:&mut Field, p:i32) -> Vec<f64>
	{ //input: p = start player
		let op:i32 = if p == 1 { 2 } else { 1 }; //other player
		let mut input:Vec<f64> = Vec::with_capacity(field.get_size() as usize);
		//1 nodes for every square: -1 enemy, 0 free, 1 own
		for val in field.get_field().iter()
		{
			if *val == p { input.push(1f64); }
			else if *val == op { input.push(-1f64); }
			else { input.push(0f64); } //empty square
		}
		//return
		input
	}

	//returns value of board position: +1.0 player wins, -1.0 other player wins, 0.0 draw or even board
	fn heur(&self, field:&mut Field, p:i32) -> f64 //p = player. translated from start player by (value * -1) if they are not same.
	{
		let op = if p == 1 {2} else {1};
		let state = field.get_state(); //return best or worst value on win/loose (neutral on tie)
		if state == -1 { return 0.0; }
		else if state == p { return VAL_MAX; }
		else if state == op { return VAL_MIN; }
		else
		{ //game running -> evaluate
			let nn = self.nn.as_ref().unwrap();
			let state = PlayerAIValue::field_to_input(field, self.startp);
			let factor = if p == self.startp { 1.0 } else { -1.0 }; //if p is not startp value has to be reversed

			let result = nn.run(&state);
			let value = factor * result[0];

			return value;
		}
	}

	fn minimax(&self, field:&mut Field, p:i32, deep:u32) -> f64
	{
		let op = if p == 1 {2} else {1};
		if deep > DEEPNESS { return self.heur(field, if deep%2 == 0 {op} else {p}); } //leaf node -> return evaluated heuristic, mechanism to get heur always for same player
		let state = field.get_state(); //return early on game end
		if state == -1 { return 0.0; }
		else if state == p { return if deep%2 == 0 {VAL_MIN} else {VAL_MAX}; }
		else if state == op { return if deep%2 == 0 {VAL_MAX} else {VAL_MIN}; }

		//else: game running -> go deeper
		let mut heur = if deep%2 == 0 { f64::INFINITY } else { f64::NEG_INFINITY };
		for i in 0..field.get_w()
		{
			if field.play(p, i)
			{
				let val = self.minimax(field, op, deep+1);
				field.undo();
				if (deep%2 == 0 && val < heur) || (deep%2 == 1 && val > heur) //min or max according to which player's turn it is
				{
					heur = val;
				}
			}
		}
		heur
	}

	fn learn_from_data(&mut self)
	{
		//use the collected data to improve the neural net
		let nn = self.nn.as_mut().unwrap();
		let mut rng = rand::thread_rng();

		//shuffle data
		let len = self.games_buffer.len();
		for _ in 0..len
		{ //n random O(1) operations on the buffer to shuffle
			let i = rng.gen::<usize>() % len;
			let item = self.games_buffer.swap_remove(i);
			self.games_buffer.push(item);
		}

		//learn
		nn.train(&self.games_buffer)
			.halt_condition(HaltCondition::Epochs(EPOCHS))
			.log_interval(None)
			.momentum(MOM)
			.rate(self.lr)
			.lambda(LAMBDA / (self.games_played as f64 + 1000.0))
			.go();

		//flush buffer
		self.games_buffer.clear();
	}
}

impl Player for PlayerAIValue
{
	#[allow(unused_variables)]
	fn init(&mut self, field:&Field, p:i32) -> bool
	{
		if DEEPNESS < 1 { return false; } //invalid player, could cause bugs else

		self.pid = p;

		self.filename = format!("AIValue-{}x{}.NN", field.get_w(), field.get_h());
		let file = File::open(&self.filename);
		if file.is_err()
		{
			//create new neural net, as it could not be loaded
			let n = field.get_size();
			self.nn = Some(NN::new(&[n, 6*n, 3*n, n, 1], Activation::PELU, Activation::Tanh)); //set size of NN layers here, be careful with activation function
			//games_played, lr already set
		}
		else
		{
			//load neural net from file (and games played)
			let mut reader = BufReader::new(file.unwrap());
			let mut datas = String::new();
			let mut nns = String::new();

			let res1 = reader.read_line(&mut datas);
			let res2 = reader.read_line(&mut nns);
			if res1.is_err() || res2.is_err() { return false; }

			let res = datas.trim().parse::<u32>();
			if res.is_err() { return false; }
			self.games_played = res.unwrap();
			self.nn = Some(NN::from_json(&nns));

			self.lr = self.get_lr();
		}

		self.initialized = true;
		true
	}

	#[allow(unused_variables)]
	fn startp(&mut self, p:i32)
	{
		self.startp = p;
	}

	fn play(&mut self, field:&mut Field) -> bool
	{
		if !self.initialized { return false; }

		let p = self.pid;
		let op = if p == 1 {2} else {1};

		//decide which action x to take
		let mut x:u32 = 0;
		let mut max = f64::NEG_INFINITY;
		//decide by evaluation
		for i in 0..field.get_w()
		{
			let mut val = f64::NEG_INFINITY;
			if field.play(p, i)
			{
				val = self.minimax(field, op, 2);
				field.undo();
			}
			if max < val || !field.is_valid_play(x)
			{
				max = val;
				x = i;
			}
		}

		//save game state for later mapping to win/loose. only if not fixed to save memory
		if !self.fixed
		{
			let state = PlayerAIValue::field_to_input(field, self.startp);
			self.current_game.push(state);
		}

		//debug
		//println!("Heur: {}", max);

		//play (actually should always be true, unless game was finished before method invocation)
		field.play(p, x)
	}

	#[allow(unused_variables)]
	fn outcome(&mut self, field:&mut Field, state:i32)
	{
		//parameters
		self.games_played += 1;
		self.lr = self.get_lr();

		//collect data and learn if not fixed (to save memory and computation else)
		if !self.fixed
		{
			//collect data
			let op:i32 = if self.startp == 1 { 2 } else { 1 }; //other player
			let mut temporal_factor = 1.0;
			let mut value = VAL_DRAW; //draw
			if state == self.startp { value = VAL_WIN; } //win
			else if state == op { value = VAL_LOSE; } //lose

			while !self.current_game.is_empty()
			{
				let state = self.current_game.pop().unwrap();
				let result = vec![temporal_factor * value];
				self.games_buffer.push((state, result));
				temporal_factor *= GAMMA; //induce unsureness at the start of the game, avoid constant value learned, prioritize fast win
			}

			//learn if it is time
			if self.games_played % LEARN_FREQ == 0
			{
				self.learn_from_data();
			}
		}
	}
}

impl Drop for PlayerAIValue
{
	fn drop(&mut self)
	{
		//if it was allowed to learn and was initialized:
		if self.initialized && !self.fixed
		{
			//learn from remaining data
			self.learn_from_data();

			//write neural net to file
			let file = File::create(&self.filename);
			if file.is_err() { println!("Warning: Could not write AIValue NN file!"); return; }
			let mut writer = BufWriter::new(file.unwrap());

			let res1 = writeln!(&mut writer, "{}", self.games_played);
			let res2 = writeln!(&mut writer, "{}", self.nn.as_mut().unwrap().to_json());
			if res1.is_err() || res2.is_err() { println!("Warning: There was an error while writing AIValue NN file!"); return; }
		}
	}
}
<|endoftext|>


<commit_before>use registry::{Enum, Registry, Cmd, Ns};
use registry::{Gl, Gles1, Gles2, Wgl, Glx, Egl};
use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;

mod ty;
pub mod global_gen;
pub mod static_gen;
pub mod struct_gen;

/// Trait for a bindings generator.
pub trait Generator {
    /// Builds the GL bindings.
    fn write(&self, &ExtCtxt, registry: &Registry, ns: Ns) -> Vec<P<ast::Item>>;
}

/// This function generates a `static name: type = value;` item.
fn gen_enum_item(ecx: &ExtCtxt, enm: &Enum, types_prefix: &str) -> P<ast::Item> {
    use syntax::ext::quote::rt::ExtParseUtils;

    // computing the name of the enum
    // if the original starts with a digit, adding an underscore prefix.
    let ident = if (enm.ident.as_slice().char_at(0)).is_digit() {
        format!("_{}", enm.ident)
    } else {
        enm.ident.clone()
    };

    // computing the type of the enum
    let ty = {
        // some enums have a value of the form `((Type)Value)` ; if this is the case, we need to
        //  replace the type of the enum (which is GLenum by default) by the type in the expression

        // matches `((a)b)`
        let regex = regex!(r"\(\((\w+)\).+\)");

        if (regex).is_match(enm.value.as_slice()) {
            // if the value is like `((Type)Value)`, then the type is `types::Type`
            regex.replace(enm.value.as_slice(), format!("{}$1", types_prefix).as_slice())

        } else if enm.value.as_slice().starts_with("\"") {
            // some values are of the form "Value" ; if this is the case, we use `&'static str`
            //  instead of `GLenum`
            "&'static str".to_string()

        } else {
            // some values are `TRUE` or `FALSE`, in which case we use `GLboolean` instead of
            //  `GLenum`
            match ident.as_slice() {
                "TRUE" | "FALSE" => format!("{}GLboolean", types_prefix),
                _ => match enm.ty {
                    Some(ref s) if s.as_slice() == "ull" => format!("{}GLuint64", types_prefix),
                    _ => format!("{}GLenum", types_prefix)
                }
            }
        }
    };

    // computing the value of the enum
    let value = {
        // similar to the type, some values are `((Type)Value)`
        // replacing "((Type)Value)" by "Value as types::Type"
        let regex = regex!(r"\(\((\w+)\)(.+)\)");
        regex.replace(enm.value.as_slice(), format!("$2 as {}$1", types_prefix).as_slice())
    };

    ecx.parse_item(format!("
        #[stable]
        #[allow(dead_code)]
        pub static {}: {} = {};"
    , ident, ty, value))
}

/// Generates all the type aliases for a namespace.
///
/// Aliases are either `pub type = ...` or `#[repr(C)] pub struct ... { ... }` and contain all the
///  things that we can't obtain from the XML files.
fn gen_type_aliases(ecx: &ExtCtxt, namespace: &Ns) -> Vec<P<ast::Item>> {
    let mut result = Vec::new();

    match *namespace {
        Gl | Gles1 | Gles2 => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
        }

        Glx => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
            result.extend(ty::build_x_aliases(ecx).into_iter());
            result.extend(ty::build_glx_aliases(ecx).into_iter());
        }

        Wgl => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
            result.extend(ty::build_win_aliases(ecx).into_iter());
            result.extend(ty::build_wgl_aliases(ecx).into_iter());
        }

        Egl => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
            result.extend(ty::build_egl_aliases(ecx).into_iter());
        }
    }

    result
}

/// Generates the list of Rust `Arg`s that a `Cmd` requires.
pub fn gen_parameters(ecx: &ExtCtxt, cmd: &Cmd) -> Vec<ast::Arg> {
    use syntax::ext::build::AstBuilder;

    cmd.params.iter()
        .map(|binding| {
            // variable name of the binding
            let ident = match binding.ident.as_slice() {
                "in" => ecx.ident_of("in_"),
                "ref" => ecx.ident_of("ref_"),
                "type" => ecx.ident_of("type_"),
                ident => ecx.ident_of(ident),
            };

            // rust type of the binding
            let ty = ty::to_rust_ty(ecx, binding.ty.as_slice());

            // returning
            // TODO: don't use call_site()?
            ecx.arg(ecx.call_site(), ident, ty)
        })
        .collect()
}

/// Generates the Rust return type of a `Cmd`.
pub fn gen_return_type(ecx: &ExtCtxt, cmd: &Cmd) -> P<ast::Ty> {
    // turn the return type into a Rust type
    let ty = ty::to_rust_ty(ecx, cmd.proto.ty.as_slice());

    // ... but there is one more step: if the Rust type ends with `c_void`, we replace it with `()`
    match ty.node {
        ast::TyPath(ref path, _ ,_)
            if path.segments.last().unwrap().identifier.as_str() == "c_void"
                => return quote_ty!(ecx, ()),
        _ => ()
    };

    ty
}

/// Generates the native symbol name of a `Cmd`.
///
/// Example results: `"glClear"`, `"wglCreateContext"`, etc.
pub fn gen_symbol_name(ns: &Ns, cmd: &Cmd) -> String {
    (match *ns {
        Gl | Gles1 | Gles2 => "gl",
        Glx => "glX",
        Wgl => "wgl",
        Egl => "egl",
    }).to_string() + cmd.proto.ident.as_slice()
}
<commit_msg>Fix non-uppercase statics<commit_after>use registry::{Enum, Registry, Cmd, Ns};
use registry::{Gl, Gles1, Gles2, Wgl, Glx, Egl};
use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;

mod ty;
pub mod global_gen;
pub mod static_gen;
pub mod struct_gen;

/// Trait for a bindings generator.
pub trait Generator {
    /// Builds the GL bindings.
    fn write(&self, &ExtCtxt, registry: &Registry, ns: Ns) -> Vec<P<ast::Item>>;
}

/// This function generates a `static name: type = value;` item.
fn gen_enum_item(ecx: &ExtCtxt, enm: &Enum, types_prefix: &str) -> P<ast::Item> {
    use syntax::ext::quote::rt::ExtParseUtils;

    // computing the name of the enum
    // if the original starts with a digit, adding an underscore prefix.
    let ident = if (enm.ident.as_slice().char_at(0)).is_digit() {
        format!("_{}", enm.ident)
    } else {
        enm.ident.clone()
    };

    // computing the type of the enum
    let ty = {
        // some enums have a value of the form `((Type)Value)` ; if this is the case, we need to
        //  replace the type of the enum (which is GLenum by default) by the type in the expression

        // matches `((a)b)`
        let regex = regex!(r"\(\((\w+)\).+\)");

        if (regex).is_match(enm.value.as_slice()) {
            // if the value is like `((Type)Value)`, then the type is `types::Type`
            regex.replace(enm.value.as_slice(), format!("{}$1", types_prefix).as_slice())

        } else if enm.value.as_slice().starts_with("\"") {
            // some values are of the form "Value" ; if this is the case, we use `&'static str`
            //  instead of `GLenum`
            "&'static str".to_string()

        } else {
            // some values are `TRUE` or `FALSE`, in which case we use `GLboolean` instead of
            //  `GLenum`
            match ident.as_slice() {
                "TRUE" | "FALSE" => format!("{}GLboolean", types_prefix),
                _ => match enm.ty {
                    Some(ref s) if s.as_slice() == "ull" => format!("{}GLuint64", types_prefix),
                    _ => format!("{}GLenum", types_prefix)
                }
            }
        }
    };

    // computing the value of the enum
    let value = {
        // similar to the type, some values are `((Type)Value)`
        // replacing "((Type)Value)" by "Value as types::Type"
        let regex = regex!(r"\(\((\w+)\)(.+)\)");
        regex.replace(enm.value.as_slice(), format!("$2 as {}$1", types_prefix).as_slice())
    };

    ecx.parse_item(format!("
        #[stable]
        #[allow(dead_code)]
        #[allow(non_uppercase_statics)]
        pub static {}: {} = {};"
    , ident, ty, value))
}

/// Generates all the type aliases for a namespace.
///
/// Aliases are either `pub type = ...` or `#[repr(C)] pub struct ... { ... }` and contain all the
///  things that we can't obtain from the XML files.
fn gen_type_aliases(ecx: &ExtCtxt, namespace: &Ns) -> Vec<P<ast::Item>> {
    let mut result = Vec::new();

    match *namespace {
        Gl | Gles1 | Gles2 => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
        }

        Glx => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
            result.extend(ty::build_x_aliases(ecx).into_iter());
            result.extend(ty::build_glx_aliases(ecx).into_iter());
        }

        Wgl => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
            result.extend(ty::build_win_aliases(ecx).into_iter());
            result.extend(ty::build_wgl_aliases(ecx).into_iter());
        }

        Egl => {
            result.extend(ty::build_gl_aliases(ecx).into_iter());
            result.extend(ty::build_egl_aliases(ecx).into_iter());
        }
    }

    result
}

/// Generates the list of Rust `Arg`s that a `Cmd` requires.
pub fn gen_parameters(ecx: &ExtCtxt, cmd: &Cmd) -> Vec<ast::Arg> {
    use syntax::ext::build::AstBuilder;

    cmd.params.iter()
        .map(|binding| {
            // variable name of the binding
            let ident = match binding.ident.as_slice() {
                "in" => ecx.ident_of("in_"),
                "ref" => ecx.ident_of("ref_"),
                "type" => ecx.ident_of("type_"),
                ident => ecx.ident_of(ident),
            };

            // rust type of the binding
            let ty = ty::to_rust_ty(ecx, binding.ty.as_slice());

            // returning
            // TODO: don't use call_site()?
            ecx.arg(ecx.call_site(), ident, ty)
        })
        .collect()
}

/// Generates the Rust return type of a `Cmd`.
pub fn gen_return_type(ecx: &ExtCtxt, cmd: &Cmd) -> P<ast::Ty> {
    // turn the return type into a Rust type
    let ty = ty::to_rust_ty(ecx, cmd.proto.ty.as_slice());

    // ... but there is one more step: if the Rust type ends with `c_void`, we replace it with `()`
    match ty.node {
        ast::TyPath(ref path, _ ,_)
            if path.segments.last().unwrap().identifier.as_str() == "c_void"
                => return quote_ty!(ecx, ()),
        _ => ()
    };

    ty
}

/// Generates the native symbol name of a `Cmd`.
///
/// Example results: `"glClear"`, `"wglCreateContext"`, etc.
pub fn gen_symbol_name(ns: &Ns, cmd: &Cmd) -> String {
    (match *ns {
        Gl | Gles1 | Gles2 => "gl",
        Glx => "glX",
        Wgl => "wgl",
        Egl => "egl",
    }).to_string() + cmd.proto.ident.as_slice()
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.

extern crate cortex;
extern crate time;
extern crate libxml;
extern crate Archive;
extern crate regex;

use std::env;
use std::str;
use regex::Regex;
use Archive::*;
use libxml::parser::Parser;
use cortex::backend::{Backend};
use cortex::data::{Corpus, Service, TaskStatus};

/// Extends all corpora registered with the CorTeX backend, with any new available sources
///  (example usage: arXiv.org releases new source bundles every month, which warrant an update at the same frequency.)
fn main() {
  let start_bundle = time::get_time();
  // Setup CorTeX backend data
  let backend = Backend::default();
  let mut input_args = env::args();
  let _ = input_args.next(); // discard the script filename
  let corpus_name = match input_args.next() {
    Some(name) => name,
    None => "arXMLiv".to_string()
  };
  let service_name = match input_args.next() {
    Some(name) => name,
    None => "tex_to_html".to_string()
  };
  let dataset_path = match input_args.next() {
    Some(path) => path,
    None => "./dataset.zip".to_string()
  };
  let corpus_placeholder = Corpus {
    id : None,
    name : corpus_name,
    path : String::new(),
    complex : true
  };
  let corpus = match backend.sync(&corpus_placeholder) {
    Ok(c) => c,
    Err(e) => {println!("Failed to load corpus: {:?}", e); return; }
  };
  let service_placeholder = Service {
    id : None,
    name : service_name,
    version : 0.1,
    inputformat : String::new(),
    outputformat : String::new(),
    inputconverter : None,
    complex : true
  };
  let service = match backend.sync(&service_placeholder) {
    Ok(s) => s,
    Err(e) => {println!("Failed to load service: {:?}", e); return; }
  };
  // Setup document parser
  let parser = Parser::default_html();
  // Set the extension regex
  let is_html_regex = Regex::new(r"\.html$").unwrap();
  let entry_name_regex = Regex::new(r"([^/]+)/([^/]+)/([^/]+)\.zip$").unwrap();
  // Set the database archive file
  let mut archive_writer_new = Writer::new().unwrap()
    .set_compression(ArchiveFilter::None) // could be imporoved later (libarchive-sys needs an upgrade ?)
    .set_format(ArchiveFormat::Zip);
    archive_writer_new.open_filename(&dataset_path.clone()).unwrap();
  // Bundle each usable status code:
  for status in vec![TaskStatus::NoProblem, TaskStatus::Warning, TaskStatus::Error].into_iter() {
    let entries = backend.entries(&corpus, &service, &status);
    println!("Entries found for severity {:?}: {:?}", status.to_key(), entries.len());
    for entry in entries {
      // Let's open the zip file and grab the result from it
      match Reader::new().unwrap()
        .support_filter_all()
        .support_format_all()
        .open_filename(&entry, 10240) {
        Ok(archive_reader) => loop {
          match archive_reader.next_header() {
            Ok(e) => {
              // Which file are we looking at?
              let pathname = e.pathname();
              let is_html = is_html_regex.is_match(&pathname);
              if !is_html { continue; }
              let mut raw_entry_data = Vec::new();
              loop {
                match archive_reader.read_data(10240) {
                  Ok(chunk) => raw_entry_data.extend(chunk.into_iter()),
                  Err(_) => {break}
                };
              }
              let is_well_formed = match str::from_utf8(&raw_entry_data) {
                Ok(some_utf_string) => {
                  if parser.is_well_formed_html(&some_utf_string) {
                    // well-formed, include in the dataset
                    true
                  }
                  else {
                    false // ill-formed, do nothing
                  }
                },
                Err(_) => false
              };
              if is_well_formed { match entry_name_regex.captures(&entry) {
                Some(cap) => {
                  let month_dir = cap.at(1).unwrap_or("monthXX");
                  let paper_dir = cap.at(2).unwrap_or("paperXX");
                  let dataset_path = status.to_key() + "/" + month_dir + "/" + paper_dir + ".html";
                  println!("Writing {:?} ...", dataset_path);

                  match archive_writer_new.write_header_new(&dataset_path, raw_entry_data.len() as i64) {
                    Ok(_) => {},
                    Err(e) => {
                      println!("Couldn't write header: {:?}", e);
                        break;
                      }
                  };
                  match archive_writer_new.write_data(raw_entry_data) {
                    Ok(_) => {},
                    Err(e) => println!("Failed to write data to {:?} because {:?}", dataset_path.clone(), e)
                  };
                }
                None => {}
              }}

              break // only one HTML file per archive
            },
            Err(_) => { break }
          }
        },
        Err(_) => {}
      }
    }
  }
  let end_bundle = time::get_time();

  let bundle_duration = (end_bundle - start_bundle).num_milliseconds();
  println!("-- Dataset bundler for corpus {:?} and service {:?} took {:?}ms", corpus.name, service.name, bundle_duration);

}<commit_msg>adding a counter for reporting<commit_after>// Copyright 2015-2016 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.

extern crate cortex;
extern crate time;
extern crate libxml;
extern crate Archive;
extern crate regex;

use std::env;
use std::str;
use regex::Regex;
use Archive::*;
use libxml::parser::Parser;
use cortex::backend::{Backend};
use cortex::data::{Corpus, Service, TaskStatus};

/// Extends all corpora registered with the CorTeX backend, with any new available sources
///  (example usage: arXiv.org releases new source bundles every month, which warrant an update at the same frequency.)
fn main() {
  let start_bundle = time::get_time();
  // Setup CorTeX backend data
  let backend = Backend::default();
  let mut input_args = env::args();
  let _ = input_args.next(); // discard the script filename
  let corpus_name = match input_args.next() {
    Some(name) => name,
    None => "arXMLiv".to_string()
  };
  let service_name = match input_args.next() {
    Some(name) => name,
    None => "tex_to_html".to_string()
  };
  let dataset_path = match input_args.next() {
    Some(path) => path,
    None => "./dataset.zip".to_string()
  };
  let corpus_placeholder = Corpus {
    id : None,
    name : corpus_name,
    path : String::new(),
    complex : true
  };
  let corpus = match backend.sync(&corpus_placeholder) {
    Ok(c) => c,
    Err(e) => {println!("Failed to load corpus: {:?}", e); return; }
  };
  let service_placeholder = Service {
    id : None,
    name : service_name,
    version : 0.1,
    inputformat : String::new(),
    outputformat : String::new(),
    inputconverter : None,
    complex : true
  };
  let service = match backend.sync(&service_placeholder) {
    Ok(s) => s,
    Err(e) => {println!("Failed to load service: {:?}", e); return; }
  };
  // Setup document parser
  let parser = Parser::default_html();
  // Set the extension regex
  let is_html_regex = Regex::new(r"\.html$").unwrap();
  let entry_name_regex = Regex::new(r"([^/]+)/([^/]+)/([^/]+)\.zip$").unwrap();
  // Set the database archive file
  let mut total_dataset_entries = 0;
  let mut archive_writer_new = Writer::new().unwrap()
    .set_compression(ArchiveFilter::None) // could be imporoved later (libarchive-sys needs an upgrade ?)
    .set_format(ArchiveFormat::Zip);
    archive_writer_new.open_filename(&dataset_path.clone()).unwrap();
  // Bundle each usable status code:
  for status in vec![TaskStatus::NoProblem, TaskStatus::Warning, TaskStatus::Error].into_iter() {
    let entries = backend.entries(&corpus, &service, &status);
    println!("Entries found for severity {:?}: {:?}", status.to_key(), entries.len());
    for entry in entries {
      // Let's open the zip file and grab the result from it
      match Reader::new().unwrap()
        .support_filter_all()
        .support_format_all()
        .open_filename(&entry, 10240) {
        Ok(archive_reader) => loop {
          match archive_reader.next_header() {
            Ok(e) => {
              // Which file are we looking at?
              let pathname = e.pathname();
              let is_html = is_html_regex.is_match(&pathname);
              if !is_html { continue; }
              let mut raw_entry_data = Vec::new();
              loop {
                match archive_reader.read_data(10240) {
                  Ok(chunk) => raw_entry_data.extend(chunk.into_iter()),
                  Err(_) => {break}
                };
              }
              let is_well_formed = match str::from_utf8(&raw_entry_data) {
                Ok(some_utf_string) => {
                  if parser.is_well_formed_html(&some_utf_string) {
                    // well-formed, include in the dataset
                    true
                  }
                  else {
                    false // ill-formed, do nothing
                  }
                },
                Err(_) => false
              };
              if is_well_formed { match entry_name_regex.captures(&entry) {
                Some(cap) => {
                  let month_dir = cap.at(1).unwrap_or("monthXX");
                  let paper_dir = cap.at(2).unwrap_or("paperXX");
                  let dataset_path = status.to_key() + "/" + month_dir + "/" + paper_dir + ".html";
                  println!("Writing {:?} ...", dataset_path);
                  total_dataset_entries += 1;
                  match archive_writer_new.write_header_new(&dataset_path, raw_entry_data.len() as i64) {
                    Ok(_) => {},
                    Err(e) => {
                      println!("Couldn't write header: {:?}", e);
                        break;
                      }
                  };
                  match archive_writer_new.write_data(raw_entry_data) {
                    Ok(_) => {},
                    Err(e) => println!("Failed to write data to {:?} because {:?}", dataset_path.clone(), e)
                  };
                }
                None => {}
              }}

              break // only one HTML file per archive
            },
            Err(_) => { break }
          }
        },
        Err(_) => {}
      }
    }
  }
  let end_bundle = time::get_time();

  let bundle_duration = (end_bundle - start_bundle).num_milliseconds();
  println!("-- Dataset bundler for corpus {:?} and service {:?} took {:?}ms", corpus.name, service.name, bundle_duration);
  println!("-- Bundled {:?} dataset entries.", total_dataset_entries);
}<|endoftext|>


<commit_before>use super::min;
use std;

/// Computes the signed distance function from a plane given as the _zero_ level set of a
/// linear function on a tetrahedron at 4 points with unit coordinates starting at (0, 0, 0) and
/// ending at (1, 1, 1), and in between exactly one coordinate changes from 0 to 1.
///
/// Inputs are `u`, the values at the vertices.
///
/// The function returns the values of the (non-signed) distance function or `None` if the zero
/// level set does not pass through the tetrahedron.
pub fn tetrahedron_anisotropic_dist<F>(
    mut u: [f64; 4],
    mut dual_norm: F,
    perm: [usize; 3],
) -> Option<[f64; 4]>
where
    F: FnMut([f64; 3]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 4 || n_pos == 4 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0.; 4]);
    }

    let g = [u[1] - u[0], u[2] - u[1], u[3] - u[2]];
    let g = [g[perm[0]], g[perm[1]], g[perm[2]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // FIXME: this requires for norm to be even
    // Support triangular anisotropies?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// Initializes the distance around the free boundary.
///
/// Based on the level set function with values `u` given on a regular grid, it computes the
/// distance from the _zero_ level set in the nodes of the triangles through which the level set
/// passes.  Stores the result in the preallocated slice `d`.
///
/// Nodes away from the boundary have their value set to `std::f64::MAX`.
///
/// Splits every cube into six tetrahedra and computes the distance on each of them.
pub fn init_anisotropic_dist_3d<F>(
    d: &mut [f64],
    u: &[f64],
    dim: (usize, usize, usize),
    mut dual_norm: F,
) where
    F: FnMut([f64; 3]) -> f64,
{
    let (nx, ny, nz) = dim;
    assert_eq!(nx * ny * nz, u.len());
    assert_eq!(nx * ny * nz, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    // split each cube into 6 tetrahedrons
    let ids = [
        [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
    ];

    let perms = [
        [0, 1, 2],
        [0, 2, 1],
        [1, 0, 2],
        [2, 0, 1],
        [1, 2, 0],
        [2, 1, 0],
    ];

    for i in 1..nx {
        for j in 1..ny {
            for k in 1..nz {
                let s = i * ny * nz + j * nz + k;
                let mut v = [0.; 4];

                for (idx, perm) in ids.iter().zip(perms.iter()) {
                    for m in 0..4 {
                        v[m] = u[s - idx[m].0 * ny * nz - idx[m].1 * nz - idx[m].2];
                    }

                    let r = tetrahedron_anisotropic_dist(v, &mut dual_norm, *perm);
                    if let Some(r) = r {
                        for m in 0..4 {
                            let q = s - idx[m].0 * ny * nz - idx[m].1 * nz - idx[m].2;
                            d[q] = min(d[q], r[m]);
                        }
                    }
                }
            }
        }
    }
}

/// Compute the anisotropic distance to the zero level set of a function on a axes-aligned
/// right triangle. The right angle is assumed to be at vertex 1.
///
/// `perm` specifies which leg of the triangle corresponds to the directions e₁ and e₂:
///
/// `[0, 1]`: 0-1 leg is parallel to e₁, 1-2 leg is parallel to e₂
/// `[1, 0]`: 0-1 leg is parallel to e₂, 1-2 leg is parallel to e₁
///
/// ```text
/// [0, 1]    [1, 0]
/// ------    ------
///
///     2      1--2
///    /|      | /
///   / |      |/
///  0--1      0
/// ```
///
fn triangle_anisotropic_dist<F>(
    mut u: [f64; 3],
    perm: [usize; 2],
    mut dual_norm: F,
) -> Option<[f64; 3]>
where
    F: FnMut([f64; 2]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 3 || n_pos == 3 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0., 0., 0.]);
    }

    let g = [u[1] - u[0], u[2] - u[1]];
    let g = [g[perm[0]], g[perm[1]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // TODO: This supports only even norms. Do we want to support triangle norms, for instance?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// As `init_dist_2d`, but for general anisotropic norm.
///
/// `dual_norm` is the __dual__ norm. It must be a positively one-homogeneous function.
pub fn init_anisotropic_dist_2d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 2]) -> f64,
{
    let (nx, ny) = dim;
    assert_eq!(nx * ny, u.len());
    assert_eq!(nx * ny, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    for j in 1..nx {
        for i in 1..ny {
            let s = j * ny + i;
            let v = [s - ny - 1, s - ny, s];
            let r = triangle_anisotropic_dist([u[v[0]], u[v[1]], u[v[2]]], [1, 0], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
            let v = [s - ny - 1, s - 1, s];
            let r = triangle_anisotropic_dist([u[v[0]], u[v[1]], u[v[2]]], [0, 1], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::norm::{DualNorm, EuclideanNorm};

    #[test]
    fn simple_triangles() {
        let triangle_dist =
            |v| triangle_anisotropic_dist(v, [0, 1], |p| EuclideanNorm.dual_norm(p));
        assert_eq!(triangle_dist([0., 0., 0.]), Some([0., 0., 0.]));
        assert_eq!(triangle_dist([1., 1., 1.]), None);
        assert_eq!(triangle_dist([-1., -1., -1.]), None);
        assert_eq!(
            triangle_dist([0., 1., 0.]),
            Some([0., 1. / (2f64).sqrt(), 0.])
        );
        assert_eq!(triangle_dist([0., -1., -1.]), Some([0., 1., 1.]));
        assert_eq!(triangle_dist([0., 1., 1.]), Some([0., 1., 1.]));
        assert_eq!(triangle_dist([1., 1., 0.]), Some([1., 1., 0.]));
        assert_eq!(triangle_dist([-1., 0., 0.]), Some([1., 0., 0.]));
        assert_eq!(
            triangle_dist([1., 0., 1.]),
            Some([1. / (2f64).sqrt(), 0., 1. / (2f64).sqrt()])
        );
    }

    #[test]
    fn anisotropic_norm_2d() {
        // Du = (1, 0)
        let u = [0., 0., 1., 1.];
        let mut d = [0.; 4];
        init_anisotropic_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0., 1., 1.]);

        // Du = (0, 1)
        let u = [0., 1., 0., 1.];
        let mut d = [0.; 4];
        init_anisotropic_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0.5, 0., 0.5]);
    }

    #[test]
    fn anisotropic_norm_3d() {
        // Du = (1, 0, 0)
        let u = [0., 0., 0., 0., 1., 1., 1., 1.];
        let mut d = [0.; 8];
        init_anisotropic_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0., 0., 1., 1., 1., 1.]);

        // Du = (0, 1, 0)
        let u = [0., 0., 1., 1., 0., 0., 1., 1.];
        let mut d = [0.; 8];
        init_anisotropic_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0.5, 0.5, 0., 0., 0.5, 0.5]);

        // Du = (0, 0, 1)
        let u = [0., 1., 0., 1., 0., 1., 0., 1.];
        let mut d = [0.; 8];
        init_anisotropic_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0.25, 0., 0.25, 0., 0.25, 0., 0.25]);
    }
}
<commit_msg>Improve init_anisotropic_dist_2d performance<commit_after>use super::min;
use std;

/// Computes the signed distance function from a plane given as the _zero_ level set of a
/// linear function on a tetrahedron at 4 points with unit coordinates starting at (0, 0, 0) and
/// ending at (1, 1, 1), and in between exactly one coordinate changes from 0 to 1.
///
/// Inputs are `u`, the values at the vertices.
///
/// The function returns the values of the (non-signed) distance function or `None` if the zero
/// level set does not pass through the tetrahedron.
pub fn tetrahedron_anisotropic_dist<F>(
    mut u: [f64; 4],
    mut dual_norm: F,
    perm: [usize; 3],
) -> Option<[f64; 4]>
where
    F: FnMut([f64; 3]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 4 || n_pos == 4 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0.; 4]);
    }

    let g = [u[1] - u[0], u[2] - u[1], u[3] - u[2]];
    let g = [g[perm[0]], g[perm[1]], g[perm[2]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // FIXME: this requires for norm to be even
    // Support triangular anisotropies?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// Initializes the distance around the free boundary.
///
/// Based on the level set function with values `u` given on a regular grid, it computes the
/// distance from the _zero_ level set in the nodes of the triangles through which the level set
/// passes.  Stores the result in the preallocated slice `d`.
///
/// Nodes away from the boundary have their value set to `std::f64::MAX`.
///
/// Splits every cube into six tetrahedra and computes the distance on each of them.
pub fn init_anisotropic_dist_3d<F>(
    d: &mut [f64],
    u: &[f64],
    dim: (usize, usize, usize),
    mut dual_norm: F,
) where
    F: FnMut([f64; 3]) -> f64,
{
    let (nx, ny, nz) = dim;
    assert_eq!(nx * ny * nz, u.len());
    assert_eq!(nx * ny * nz, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    // split each cube into 6 tetrahedrons
    let ids = [
        [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
    ];

    let perms = [
        [0, 1, 2],
        [0, 2, 1],
        [1, 0, 2],
        [2, 0, 1],
        [1, 2, 0],
        [2, 1, 0],
    ];

    for i in 1..nx {
        for j in 1..ny {
            for k in 1..nz {
                let s = i * ny * nz + j * nz + k;
                let mut v = [0.; 4];

                for (idx, perm) in ids.iter().zip(perms.iter()) {
                    for m in 0..4 {
                        v[m] = u[s - idx[m].0 * ny * nz - idx[m].1 * nz - idx[m].2];
                    }

                    let r = tetrahedron_anisotropic_dist(v, &mut dual_norm, *perm);
                    if let Some(r) = r {
                        for m in 0..4 {
                            let q = s - idx[m].0 * ny * nz - idx[m].1 * nz - idx[m].2;
                            d[q] = min(d[q], r[m]);
                        }
                    }
                }
            }
        }
    }
}

/// Compute the anisotropic distance to the zero level set of a function on a axes-aligned
/// right triangle. The right angle is assumed to be at vertex 1.
///
/// `perm` specifies which leg of the triangle corresponds to the directions e₁ and e₂:
///
/// `[0, 1]`: 0-1 leg is parallel to e₁, 1-2 leg is parallel to e₂
/// `[1, 0]`: 0-1 leg is parallel to e₂, 1-2 leg is parallel to e₁
///
/// ```text
/// [0, 1]    [1, 0]
/// ------    ------
///
///     2      1--2
///    /|      | /
///   / |      |/
///  0--1      0
/// ```
///
fn triangle_anisotropic_dist<F>(
    mut u: [f64; 3],
    perm: [usize; 2],
    mut dual_norm: F,
) -> Option<[f64; 3]>
where
    F: FnMut([f64; 2]) -> f64,
{
    // check if sign differs (level set goes throught the triangle)
    if (u[0] > 0. && u[1] > 0. && u[2] > 0.) || (u[0] < 0. && u[1] < 0. && u[2] < 0.) {
        return None;
    }

    let g = [u[1] - u[0], u[2] - u[1]];
    let g = [g[perm[0]], g[perm[1]]];
    let norm = dual_norm(g);
    // all values are zero
    if norm == 0. {
        return Some([0., 0., 0.]);
    }
    let g_norm_rcp = 1. / norm;

    // TODO: This supports only even norms. Do we want to support triangle norms, for instance?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// As `init_dist_2d`, but for general anisotropic norm.
///
/// `dual_norm` is the __dual__ norm. It must be a positively one-homogeneous function.
pub fn init_anisotropic_dist_2d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 2]) -> f64,
{
    let (nx, ny) = dim;
    assert_eq!(nx * ny, u.len());
    assert_eq!(nx * ny, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    for j in 1..nx {
        for i in 1..ny {
            let s = j * ny + i;
            let v = [s - ny - 1, s - ny, s];
            let r = triangle_anisotropic_dist([u[v[0]], u[v[1]], u[v[2]]], [1, 0], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
            let v = [s - ny - 1, s - 1, s];
            let r = triangle_anisotropic_dist([u[v[0]], u[v[1]], u[v[2]]], [0, 1], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::norm::{DualNorm, EuclideanNorm};

    #[test]
    fn simple_triangles() {
        let triangle_dist =
            |v| triangle_anisotropic_dist(v, [0, 1], |p| EuclideanNorm.dual_norm(p));
        assert_eq!(triangle_dist([0., 0., 0.]), Some([0., 0., 0.]));
        assert_eq!(triangle_dist([1., 1., 1.]), None);
        assert_eq!(triangle_dist([-1., -1., -1.]), None);
        assert_eq!(
            triangle_dist([0., 1., 0.]),
            Some([0., 1. / (2f64).sqrt(), 0.])
        );
        assert_eq!(triangle_dist([0., -1., -1.]), Some([0., 1., 1.]));
        assert_eq!(triangle_dist([0., 1., 1.]), Some([0., 1., 1.]));
        assert_eq!(triangle_dist([1., 1., 0.]), Some([1., 1., 0.]));
        assert_eq!(triangle_dist([-1., 0., 0.]), Some([1., 0., 0.]));
        assert_eq!(
            triangle_dist([1., 0., 1.]),
            Some([1. / (2f64).sqrt(), 0., 1. / (2f64).sqrt()])
        );
    }

    #[test]
    fn anisotropic_norm_2d() {
        // Du = (1, 0)
        let u = [0., 0., 1., 1.];
        let mut d = [0.; 4];
        init_anisotropic_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0., 1., 1.]);

        // Du = (0, 1)
        let u = [0., 1., 0., 1.];
        let mut d = [0.; 4];
        init_anisotropic_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0.5, 0., 0.5]);
    }

    #[test]
    fn anisotropic_norm_3d() {
        // Du = (1, 0, 0)
        let u = [0., 0., 0., 0., 1., 1., 1., 1.];
        let mut d = [0.; 8];
        init_anisotropic_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0., 0., 1., 1., 1., 1.]);

        // Du = (0, 1, 0)
        let u = [0., 0., 1., 1., 0., 0., 1., 1.];
        let mut d = [0.; 8];
        init_anisotropic_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0.5, 0.5, 0., 0., 0.5, 0.5]);

        // Du = (0, 0, 1)
        let u = [0., 1., 0., 1., 0., 1., 0., 1.];
        let mut d = [0.; 8];
        init_anisotropic_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0.25, 0., 0.25, 0., 0.25, 0., 0.25]);
    }
}
<|endoftext|>