<commit_before>use cpp_method::*;
use cpp_operators::CppOperator;
use cpp_data::CppVisibility;
use cpp_type::*;
use cpp_ffi_function_argument::CppFfiArgumentMeaning;
use cpp_ffi_type::IndirectionChange;

#[test]
fn cpp_method_scope() {
  let a1 = CppMethodScope::Global;
  assert!(a1.class_name().is_none());

  let a2 = CppMethodScope::Class("Class1".to_string());
  assert_eq!(a2.class_name(), Some(&"Class1".to_string()));
}

#[test]
fn cpp_method_kind() {
  assert!(CppMethodKind::Operator(CppOperator::Assignment).is_operator());
  assert!(!CppMethodKind::Operator(CppOperator::Assignment).is_destructor());
  assert!(!CppMethodKind::Operator(CppOperator::Assignment).is_constructor());
  assert!(!CppMethodKind::Operator(CppOperator::Assignment).is_regular());

  assert!(!CppMethodKind::Constructor.is_operator());
  assert!(!CppMethodKind::Constructor.is_destructor());
  assert!(CppMethodKind::Constructor.is_constructor());
  assert!(!CppMethodKind::Constructor.is_regular());

  assert!(!CppMethodKind::Destructor.is_operator());
  assert!(CppMethodKind::Destructor.is_destructor());
  assert!(!CppMethodKind::Destructor.is_constructor());
  assert!(!CppMethodKind::Destructor.is_regular());

  assert!(!CppMethodKind::Regular.is_operator());
  assert!(!CppMethodKind::Regular.is_destructor());
  assert!(!CppMethodKind::Regular.is_constructor());
  assert!(CppMethodKind::Regular.is_regular());
}

fn empty_regular_method() -> CppMethod {
  CppMethod {
    name: String::new(),
    scope: CppMethodScope::Global,
    is_virtual: false,
    is_pure_virtual: false,
    is_const: false,
    is_static: false,
    visibility: CppVisibility::Public,
    is_signal: false,
    return_type: None,
    class_type: None,
    kind: CppMethodKind::Regular,
    arguments: vec![],
    allows_variable_arguments: false,
    include_file: String::new(),
    origin_location: None,
    template_arguments: None,
  }
}

#[test]
fn argument_types_equal1() {
  let method1 = empty_regular_method();
  let method2 = empty_regular_method();
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal2() {
  let mut method1 = empty_regular_method();
  let method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal3() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "x".to_string(),
    has_default_value: false,
  });
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal4() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: true,
  });
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal5() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Enum { name: "Enum1".to_string() },
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal6() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::Ptr,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal7() {
  let mut method1 = empty_regular_method();
  let int = CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  };
  let mut method2 = empty_regular_method();
  method1.arguments.push(int.clone());
  method2.arguments.push(int.clone());
  method2.arguments.push(int.clone());
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal8() {
  let mut method1 = empty_regular_method();
  let method2 = empty_regular_method();
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn needs_allocation_place_variants() {
  let mut method1 = empty_regular_method();
  assert!(!method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Constructor;
  assert!(method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Destructor;
  assert!(method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Operator(CppOperator::Assignment);
  assert!(!method1.needs_allocation_place_variants());
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  assert!(!method1.needs_allocation_place_variants());
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::Class {
      name: "QRect".to_string(),
      template_arguments: None,
    },
  });
  assert!(method1.needs_allocation_place_variants());
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::Ptr,
    is_const: false,
    base: CppTypeBase::Class {
      name: "QRect".to_string(),
      template_arguments: None,
    },
  });
  assert!(!method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Regular;
  method1.return_type = None;
  assert!(!method1.needs_allocation_place_variants());
}

#[test]
fn c_signature_empty() {
  let mut method1 = empty_regular_method();
  method1.return_type = Some(CppType::void());
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.is_empty());
  assert!(r.return_type.ffi_type.is_void());
}

#[test]
fn c_signature_simple_func() {
  let mut method1 = empty_regular_method();
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Enum { name: "Enum1".to_string() },
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.len() == 1);
  assert_eq!(r.arguments[0].name, "arg1");
  assert_eq!(r.arguments[0].argument_type.ffi_type,
             method1.arguments[0].argument_type);
  assert_eq!(r.arguments[0].argument_type.conversion, IndirectionChange::NoChange);
  assert_eq!(r.arguments[0].meaning, CppFfiArgumentMeaning::Argument(0));
  assert_eq!(r.return_type.ffi_type, method1.return_type.unwrap());
}

#[test]
fn c_signature_method_with_this() {
  let mut method1 = empty_regular_method();
  method1.scope = CppMethodScope::Class("MyClass".to_string());
  method1.class_type = Some(CppTypeBase::Class {
    name: "MyClass".to_string(),
    template_arguments: None,
  });
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Class {
        name: "MyClass2".to_string(),
        template_arguments: None,
      },
    },
    name: "my_arg".to_string(),
    has_default_value: false,
  });
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.len() == 2);
  assert_eq!(r.arguments[0].name, "this_ptr");
  assert_eq!(r.arguments[0].argument_type.ffi_type.base,
             method1.class_type.unwrap());
  assert_eq!(r.arguments[0].argument_type.ffi_type.indirection,
             CppTypeIndirection::Ptr);
  assert_eq!(r.arguments[0].argument_type.conversion, IndirectionChange::NoChange);
  assert_eq!(r.arguments[0].meaning, CppFfiArgumentMeaning::This);

  assert_eq!(r.arguments[1].name, "my_arg");
  assert_eq!(r.arguments[1].argument_type.ffi_type.base,
             method1.arguments[0].argument_type.base);
  assert_eq!(r.arguments[1].argument_type.ffi_type.indirection,
             CppTypeIndirection::Ptr);
  assert_eq!(r.arguments[1].argument_type.conversion, IndirectionChange::ValueToPointer);
  assert_eq!(r.arguments[1].meaning, CppFfiArgumentMeaning::Argument(0));
  assert_eq!(r.return_type.ffi_type, method1.return_type.unwrap());
}
<commit_msg>more tests<commit_after>use cpp_method::*;
use cpp_operators::CppOperator;
use cpp_data::CppVisibility;
use cpp_type::*;
use cpp_ffi_function_argument::CppFfiArgumentMeaning;
use cpp_ffi_type::IndirectionChange;

#[test]
fn cpp_method_scope() {
  let a1 = CppMethodScope::Global;
  assert!(a1.class_name().is_none());

  let a2 = CppMethodScope::Class("Class1".to_string());
  assert_eq!(a2.class_name(), Some(&"Class1".to_string()));
}

#[test]
fn cpp_method_kind() {
  assert!(CppMethodKind::Operator(CppOperator::Assignment).is_operator());
  assert!(!CppMethodKind::Operator(CppOperator::Assignment).is_destructor());
  assert!(!CppMethodKind::Operator(CppOperator::Assignment).is_constructor());
  assert!(!CppMethodKind::Operator(CppOperator::Assignment).is_regular());

  assert!(!CppMethodKind::Constructor.is_operator());
  assert!(!CppMethodKind::Constructor.is_destructor());
  assert!(CppMethodKind::Constructor.is_constructor());
  assert!(!CppMethodKind::Constructor.is_regular());

  assert!(!CppMethodKind::Destructor.is_operator());
  assert!(CppMethodKind::Destructor.is_destructor());
  assert!(!CppMethodKind::Destructor.is_constructor());
  assert!(!CppMethodKind::Destructor.is_regular());

  assert!(!CppMethodKind::Regular.is_operator());
  assert!(!CppMethodKind::Regular.is_destructor());
  assert!(!CppMethodKind::Regular.is_constructor());
  assert!(CppMethodKind::Regular.is_regular());
}

fn empty_regular_method() -> CppMethod {
  CppMethod {
    name: String::new(),
    scope: CppMethodScope::Global,
    is_virtual: false,
    is_pure_virtual: false,
    is_const: false,
    is_static: false,
    visibility: CppVisibility::Public,
    is_signal: false,
    return_type: None,
    class_type: None,
    kind: CppMethodKind::Regular,
    arguments: vec![],
    allows_variable_arguments: false,
    include_file: String::new(),
    origin_location: None,
    template_arguments: None,
  }
}

#[test]
fn argument_types_equal1() {
  let method1 = empty_regular_method();
  let method2 = empty_regular_method();
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal2() {
  let mut method1 = empty_regular_method();
  let method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal3() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "x".to_string(),
    has_default_value: false,
  });
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal4() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: true,
  });
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal5() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Enum { name: "Enum1".to_string() },
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal6() {
  let mut method1 = empty_regular_method();
  let mut method2 = empty_regular_method();
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  method2.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::Ptr,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal7() {
  let mut method1 = empty_regular_method();
  let int = CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
    },
    name: "arg1".to_string(),
    has_default_value: false,
  };
  let mut method2 = empty_regular_method();
  method1.arguments.push(int.clone());
  method2.arguments.push(int.clone());
  method2.arguments.push(int.clone());
  assert!(!method1.argument_types_equal(&method2));
  assert!(!method2.argument_types_equal(&method1));
}

#[test]
fn argument_types_equal8() {
  let mut method1 = empty_regular_method();
  let method2 = empty_regular_method();
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  assert!(method1.argument_types_equal(&method2));
  assert!(method2.argument_types_equal(&method1));
}

#[test]
fn needs_allocation_place_variants() {
  let mut method1 = empty_regular_method();
  assert!(!method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Constructor;
  assert!(method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Destructor;
  assert!(method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Operator(CppOperator::Assignment);
  assert!(!method1.needs_allocation_place_variants());
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  assert!(!method1.needs_allocation_place_variants());
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::Class {
      name: "QRect".to_string(),
      template_arguments: None,
    },
  });
  assert!(method1.needs_allocation_place_variants());
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::Ptr,
    is_const: false,
    base: CppTypeBase::Class {
      name: "QRect".to_string(),
      template_arguments: None,
    },
  });
  assert!(!method1.needs_allocation_place_variants());
  method1.kind = CppMethodKind::Regular;
  method1.return_type = None;
  assert!(!method1.needs_allocation_place_variants());
}

#[test]
fn c_signature_empty() {
  let mut method1 = empty_regular_method();
  method1.return_type = Some(CppType::void());
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.is_empty());
  assert!(r.return_type.ffi_type.is_void());
}

#[test]
fn c_signature_simple_func() {
  let mut method1 = empty_regular_method();
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Enum { name: "Enum1".to_string() },
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.len() == 1);
  assert_eq!(r.arguments[0].name, "arg1");
  assert_eq!(r.arguments[0].argument_type.ffi_type,
             method1.arguments[0].argument_type);
  assert_eq!(r.arguments[0].argument_type.conversion,
             IndirectionChange::NoChange);
  assert_eq!(r.arguments[0].meaning, CppFfiArgumentMeaning::Argument(0));
  assert_eq!(r.return_type.ffi_type, method1.return_type.unwrap());
  assert_eq!(r.return_type.conversion, IndirectionChange::NoChange);
}

#[test]
fn c_signature_method_with_this() {
  let mut method1 = empty_regular_method();
  method1.scope = CppMethodScope::Class("MyClass".to_string());
  method1.class_type = Some(CppTypeBase::Class {
    name: "MyClass".to_string(),
    template_arguments: None,
  });
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Class {
        name: "MyClass2".to_string(),
        template_arguments: None,
      },
    },
    name: "my_arg".to_string(),
    has_default_value: false,
  });
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.len() == 2);
  assert_eq!(r.arguments[0].name, "this_ptr");
  assert_eq!(r.arguments[0].argument_type.ffi_type.base,
             method1.class_type.unwrap());
  assert_eq!(r.arguments[0].argument_type.ffi_type.indirection,
             CppTypeIndirection::Ptr);
  assert_eq!(r.arguments[0].argument_type.conversion,
             IndirectionChange::NoChange);
  assert_eq!(r.arguments[0].meaning, CppFfiArgumentMeaning::This);

  assert_eq!(r.arguments[1].name, "my_arg");
  assert_eq!(r.arguments[1].argument_type.ffi_type.base,
             method1.arguments[0].argument_type.base);
  assert_eq!(r.arguments[1].argument_type.ffi_type.indirection,
             CppTypeIndirection::Ptr);
  assert_eq!(r.arguments[1].argument_type.conversion,
             IndirectionChange::ValueToPointer);
  assert_eq!(r.arguments[1].meaning, CppFfiArgumentMeaning::Argument(0));
  assert_eq!(r.return_type.ffi_type, method1.return_type.unwrap());
}

#[test]
fn c_signature_static_method() {
  let mut method1 = empty_regular_method();
  method1.scope = CppMethodScope::Class("MyClass".to_string());
  method1.class_type = Some(CppTypeBase::Class {
    name: "MyClass".to_string(),
    template_arguments: None,
  });
  method1.is_static = true;
  method1.return_type = Some(CppType {
    indirection: CppTypeIndirection::None,
    is_const: false,
    base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
  });
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::None,
      is_const: false,
      base: CppTypeBase::Enum { name: "Enum1".to_string() },
    },
    name: "arg1".to_string(),
    has_default_value: false,
  });
  let r = method1.c_signature(ReturnValueAllocationPlace::NotApplicable).unwrap();
  assert!(r.arguments.len() == 1);
  assert_eq!(r.arguments[0].name, "arg1");
  assert_eq!(r.arguments[0].argument_type.ffi_type,
             method1.arguments[0].argument_type);
  assert_eq!(r.arguments[0].argument_type.conversion,
             IndirectionChange::NoChange);
  assert_eq!(r.arguments[0].meaning, CppFfiArgumentMeaning::Argument(0));
  assert_eq!(r.return_type.ffi_type, method1.return_type.unwrap());
}

#[test]
fn c_signature_constructor() {
  let mut method1 = empty_regular_method();
  method1.kind = CppMethodKind::Constructor;
  method1.scope = CppMethodScope::Class("MyClass".to_string());
  method1.class_type = Some(CppTypeBase::Class {
    name: "MyClass".to_string(),
    template_arguments: None,
  });
  method1.arguments.push(CppFunctionArgument {
    argument_type: CppType {
      indirection: CppTypeIndirection::Ref,
      is_const: true,
      base: CppTypeBase::Enum { name: "Enum1".to_string() },
    },
    name: "arg1".to_string(),
    has_default_value: true,
  });
  let r_stack = method1.c_signature(ReturnValueAllocationPlace::Stack).unwrap();
  assert!(r_stack.arguments.len() == 2);
  assert_eq!(r_stack.arguments[0].name, "arg1");
  assert_eq!(r_stack.arguments[0].argument_type.ffi_type,
             CppType {
               indirection: CppTypeIndirection::Ptr,
               is_const: true,
               base: CppTypeBase::Enum { name: "Enum1".to_string() },
             });
  assert_eq!(r_stack.arguments[0].argument_type.conversion,
             IndirectionChange::ReferenceToPointer);
  assert_eq!(r_stack.arguments[0].meaning, CppFfiArgumentMeaning::Argument(0));

  assert_eq!(r_stack.arguments[1].name, "output");
  assert_eq!(r_stack.arguments[1].argument_type.ffi_type,
             CppType {
               indirection: CppTypeIndirection::Ptr,
               is_const: false,
               base: CppTypeBase::Class {
                 name: "MyClass".to_string(),
                 template_arguments: None,
               },
             });
  assert_eq!(r_stack.arguments[1].argument_type.conversion,
             IndirectionChange::ValueToPointer);
  assert_eq!(r_stack.arguments[1].meaning, CppFfiArgumentMeaning::ReturnValue);

  assert!(r_stack.return_type.ffi_type.is_void());

  let r_heap = method1.c_signature(ReturnValueAllocationPlace::Heap).unwrap();
  assert!(r_heap.arguments.len() == 1);
  assert_eq!(r_heap.arguments[0].name, "arg1");
  assert_eq!(r_heap.arguments[0].argument_type.ffi_type,
             CppType {
               indirection: CppTypeIndirection::Ptr,
               is_const: true,
               base: CppTypeBase::Enum { name: "Enum1".to_string() },
             });
  assert_eq!(r_heap.arguments[0].argument_type.conversion,
             IndirectionChange::ReferenceToPointer);
  assert_eq!(r_heap.arguments[0].meaning, CppFfiArgumentMeaning::Argument(0));
  assert_eq!(r_heap.return_type.ffi_type,
             CppType {
               indirection: CppTypeIndirection::Ptr,
               is_const: false,
               base: CppTypeBase::Class {
                 name: "MyClass".to_string(),
                 template_arguments: None,
               },
             });
  assert_eq!(r_heap.return_type.conversion,
             IndirectionChange::ValueToPointer);
}

// TODO: add tests for:
// - destructor
// - class method returning a class value
// - class method with a class value argument but int return type
<|endoftext|>


<commit_before>use std::{mem, ptr};
use std::ffi::CStr;
use std::fs::File;
use std::io::{self, Read, Write};
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::str::FromStr;

use libc::{AF_INET, SOCK_DGRAM, c_char, c_void, close, connect, getsockopt, sockaddr, socket, socklen_t};

use common::error::*;
use tun::Tun;
use tun::configuration::{Configurable, Configuration};
use tun::sockaddr::SockAddr;

use super::sys::*;

pub fn create(configuration: &Configuration) -> Result<Device> {
    Device::from_configuration(&configuration)
}

const IPV4: u8 = 4u8;
const IPV6: u8 = 6u8;
const IP_HEADER_LEN: usize = 4;
const IPV4_HEADER: [u8; 4] = [0, 0, 0, 2];
const IPV6_HEADER: [u8; 4] = [0, 0, 0, 10];

#[derive(Debug)]
pub struct Device {
    name: String,
    tun: File,
    ctl: File,
}

impl Device {
    pub unsafe fn request(&self) -> ifreq {
        let mut req: ifreq = mem::zeroed();
        ptr::copy_nonoverlapping(self.name.as_ptr() as *const c_char, req.ifr_name.as_mut_ptr(), self.name.len());

        req
    }

    pub unsafe fn alias_request(&self) -> ifaliasreq {
        let mut alias_req: ifaliasreq = mem::zeroed();
        ptr::copy_nonoverlapping(self.name.as_ptr() as *const c_char,
                                 alias_req.ifra_name.as_mut_ptr(),
                                 self.name.len());

        alias_req
    }

    pub fn ipv4(&mut self, addr: Ipv4Addr, broadaddr: Ipv4Addr, mask: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut alias_req = self.alias_request();
            alias_req.ifra_addr = SockAddr::from(addr).into();
            alias_req.ifra_broadaddr = SockAddr::from(broadaddr).into();
            alias_req.ifra_mask = SockAddr::from(mask).into();

            if siocaifaddr(self.ctl.as_raw_fd(), &alias_req) < 0 {
                return Err(io::Error::last_os_error().into());
            }
            Ok(())
        }
    }

    pub fn delete_addr(&mut self) -> Result<()> {
        unsafe {
            let req = self.request();

            if siocdifaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }
            Ok(())
        }
    }
}

impl Read for Device {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.tun.read(buf)
    }
}

impl Write for Device {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if !buf.len() > 0 {
            return Ok(0);
        };
        let mut data = Vec::with_capacity(buf.len() + IP_HEADER_LEN);

        match buf[0] & 0xF {
            IPV4 => {
                data.extend_from_slice(&IPV4_HEADER);
            }
            IPV6 => data.extend_from_slice(&IPV6_HEADER),
            _ => {}
        };

        match self.tun.write(&data) {
            Ok(len) => {
                Ok(if len > IP_HEADER_LEN {
                       len - IP_HEADER_LEN
                   } else {
                       0
                   })
            }
            Err(e) => Err(e),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        self.tun.flush()
    }
}

impl AsRawFd for Device {
    fn as_raw_fd(&self) -> RawFd {
        self.tun.as_raw_fd()
    }
}

impl Tun for Device {
    fn name(&self) -> &str {
        &self.name
    }

    fn address(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::new(&req.ifr_ifru.ifru_addr).map(Into::into)
        }
    }
    fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_addr = SockAddr::from(value).into();

            if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn broadcast(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::new(&req.ifr_ifru.ifru_broadaddr).map(Into::into)
        }
    }
    fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_broadaddr = SockAddr::from(value).into();

            if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn destination(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::new(&req.ifr_ifru.ifru_dstaddr).map(Into::into)
        }
    }
    fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_dstaddr = SockAddr::from(value).into();

            if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn netmask(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::unchecked(&req.ifr_ifru.ifru_addr).map(Into::into)
        }
    }
    fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_addr = SockAddr::from(value).into();

            if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn mtu(&self) -> Result<i32> {
        unsafe {
            let mut req = self.request();

            if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(req.ifr_ifru.ifru_mtu)
        }
    }
    fn set_mtu(&mut self, value: i32) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_mtu = value;

            if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn flags(&self) -> Result<i16> {
        unsafe {
            let mut req = self.request();

            if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(req.ifr_ifru.ifru_flags)
        }
    }
    fn set_flags(&mut self, value: i16) -> Result<()> {
        let origin_flags = self.flags()?;
        let mut value = value;

        unsafe {
            let mut req = self.request();

            req.ifr_ifru.ifru_flags = if value < 0 {
                value = -value;
                origin_flags & !value
            } else {
                origin_flags | value
            };

            if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn set_enabled(&mut self, value: bool) -> Result<()> {
        if value {
            return self.set_flags(IFF_UP | IFF_RUNNING);
        }

        self.set_flags(-IFF_UP)
    }
}

impl Configurable for Device {
    fn from_configuration(configuration: &Configuration) -> Result<Self> {
        let dev_id = match configuration.name.as_ref() {
            Some(name) => {
                if name.len() > IFNAMSIZ {
                    return Err(ErrorKind::TunNameTooLong.into());
                }
                if !name.starts_with("utun") {
                    return Err(ErrorKind::InvalidTunName.into());
                }
                u8::from_str(&name["utun".len()..])? as u8
            }

            None => 0,
        };

        let tun = unsafe { socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL) };
        if tun < 0 {
            return Err(io::Error::last_os_error().into());
        }

        let mut info = ctl_info {
            ctl_id: 0,
            ctl_name: {
                let mut bufer = [0u8; 96];
                bufer[..UTUN_CONTROL_NAME.len()].clone_from_slice(UTUN_CONTROL_NAME.as_bytes());
                bufer
            },
        };

        unsafe {
            if ctliocginfo(tun, &mut info as *mut _ as *mut _) < 0 {
                close(tun);
                return Err(io::Error::last_os_error().into());
            }
        }

        let addr = sockaddr_ctl {
            sc_id: info.ctl_id,
            sc_len: mem::size_of::<sockaddr_ctl>() as u8,
            sc_family: AF_SYSTEM,
            ss_sysaddr: AF_SYS_CONTROL,
            sc_unit: dev_id as u32 + 1,
            sc_reserved: [0; 5],
        };

        if unsafe {
            connect(tun,
                    &addr as *const sockaddr_ctl as *const sockaddr,
                    mem::size_of_val(&addr) as socklen_t)
        } < 0
        {
            return Err(io::Error::last_os_error().into());
        }

        let mut name_buf = [0u8; 64];
        let mut name_length: socklen_t = 64;
        if unsafe {
            getsockopt(tun,
                       SYSPROTO_CONTROL,
                       UTUN_OPT_IFNAME,
                       &mut name_buf as *mut _ as *mut c_void,
                       &mut name_length as *mut socklen_t)
        } < 0
        {
            return Err(io::Error::last_os_error().into());
        }

        let ctl = unsafe { socket(AF_INET, SOCK_DGRAM, 0) };
        if ctl < 0 {
            return Err(io::Error::last_os_error().into());
        }

        let mut device = unsafe {
            Device {
                name: CStr::from_ptr(name_buf.as_ptr() as *const c_char).to_string_lossy().into(),
                tun: File::from_raw_fd(tun),
                ctl: File::from_raw_fd(ctl),
            }
        };

        device.configure(configuration)?;
        Ok(device)
    }
    fn configure(&mut self, configuration: &Configuration) -> Result<()> {
        if let Some(ip) = configuration.address {
            self.set_address(ip)?;
        }

        if let Some(ip) = configuration.destination {
            self.set_destination(ip)?;
        }

        if let Some(ip) = configuration.broadcast {
            self.set_broadcast(ip)?;
        }

        if let Some(ip) = configuration.netmask {
            self.set_netmask(ip)?;
        }

        if let Some(mtu) = configuration.mtu {
            self.set_mtu(mtu)?;
        }

        self.set_enabled(configuration.enabled)?;

        Ok(())
    }
}

include!("../unix/mio.rs.in");
<commit_msg>fix tun read and write in macos. fixes #12<commit_after>use std::{mem, ptr};
use std::ffi::CStr;
use std::fs::File;
use std::io::{self, Read, Write};
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::str::FromStr;

use libc::{AF_INET, SOCK_DGRAM, c_char, c_void, close, connect, getsockopt, sockaddr, socket, socklen_t};

use common::error::*;
use tun::Tun;
use tun::configuration::{Configurable, Configuration};
use tun::sockaddr::SockAddr;

use super::sys::*;

pub fn create(configuration: &Configuration) -> Result<Device> {
    Device::from_configuration(&configuration)
}

const IPV4: u8 = 4u8;
const IPV6: u8 = 6u8;
const IP_HEADER_LEN: usize = 4;
const IPV4_HEADER: [u8; 4] = [0, 0, 0, 2];
const IPV6_HEADER: [u8; 4] = [0, 0, 0, 10];

#[derive(Debug)]
pub struct Device {
    name: String,
    tun: File,
    ctl: File,
}

impl Device {
    pub unsafe fn request(&self) -> ifreq {
        let mut req: ifreq = mem::zeroed();
        ptr::copy_nonoverlapping(self.name.as_ptr() as *const c_char, req.ifr_name.as_mut_ptr(), self.name.len());

        req
    }

    pub unsafe fn alias_request(&self) -> ifaliasreq {
        let mut alias_req: ifaliasreq = mem::zeroed();
        ptr::copy_nonoverlapping(self.name.as_ptr() as *const c_char,
                                 alias_req.ifra_name.as_mut_ptr(),
                                 self.name.len());

        alias_req
    }

    pub fn ipv4(&mut self, addr: Ipv4Addr, broadaddr: Ipv4Addr, mask: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut alias_req = self.alias_request();
            alias_req.ifra_addr = SockAddr::from(addr).into();
            alias_req.ifra_broadaddr = SockAddr::from(broadaddr).into();
            alias_req.ifra_mask = SockAddr::from(mask).into();

            if siocaifaddr(self.ctl.as_raw_fd(), &alias_req) < 0 {
                return Err(io::Error::last_os_error().into());
            }
            Ok(())
        }
    }

    pub fn delete_addr(&mut self) -> Result<()> {
        unsafe {
            let req = self.request();

            if siocdifaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }
            Ok(())
        }
    }
}

impl Read for Device {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let mut rbuf = Vec::with_capacity(buf.len());
        let rsize = self.tun.read(&mut rbuf)?;
        let mut offset = 0;
        if rsize > IP_HEADER_LEN {
            offset += IP_HEADER_LEN;
        };
        let csize = rsize - offset;

        buf[..csize].copy_from_slice(&rbuf[offset..]);
        Ok(csize)
    }
}

impl Write for Device {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if !buf.len() > 0 {
            return Ok(0);
        };
        let mut wbuf = Vec::with_capacity(buf.len() + IP_HEADER_LEN);

        match buf[0] & 0xF {
            IPV4 => wbuf.extend_from_slice(&IPV4_HEADER),
            IPV6 => wbuf.extend_from_slice(&IPV6_HEADER),
            _ => {}
        };
        wbuf.extend_from_slice(&buf);

        let wsize = self.tun.write(&wbuf)?;
        Ok(if wsize > IP_HEADER_LEN {
               wsize - IP_HEADER_LEN
           } else {
               0
           })
    }

    fn flush(&mut self) -> io::Result<()> {
        self.tun.flush()
    }
}

impl AsRawFd for Device {
    fn as_raw_fd(&self) -> RawFd {
        self.tun.as_raw_fd()
    }
}

impl Tun for Device {
    fn name(&self) -> &str {
        &self.name
    }

    fn address(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::new(&req.ifr_ifru.ifru_addr).map(Into::into)
        }
    }
    fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_addr = SockAddr::from(value).into();

            if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn broadcast(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::new(&req.ifr_ifru.ifru_broadaddr).map(Into::into)
        }
    }
    fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_broadaddr = SockAddr::from(value).into();

            if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn destination(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::new(&req.ifr_ifru.ifru_dstaddr).map(Into::into)
        }
    }
    fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_dstaddr = SockAddr::from(value).into();

            if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn netmask(&self) -> Result<Ipv4Addr> {
        unsafe {
            let mut req = self.request();

            if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            SockAddr::unchecked(&req.ifr_ifru.ifru_addr).map(Into::into)
        }
    }
    fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_addr = SockAddr::from(value).into();

            if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn mtu(&self) -> Result<i32> {
        unsafe {
            let mut req = self.request();

            if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(req.ifr_ifru.ifru_mtu)
        }
    }
    fn set_mtu(&mut self, value: i32) -> Result<()> {
        unsafe {
            let mut req = self.request();
            req.ifr_ifru.ifru_mtu = value;

            if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn flags(&self) -> Result<i16> {
        unsafe {
            let mut req = self.request();

            if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(req.ifr_ifru.ifru_flags)
        }
    }
    fn set_flags(&mut self, value: i16) -> Result<()> {
        let origin_flags = self.flags()?;
        let mut value = value;

        unsafe {
            let mut req = self.request();

            req.ifr_ifru.ifru_flags = if value < 0 {
                value = -value;
                origin_flags & !value
            } else {
                origin_flags | value
            };

            if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
                return Err(io::Error::last_os_error().into());
            }

            Ok(())
        }
    }

    fn set_enabled(&mut self, value: bool) -> Result<()> {
        if value {
            return self.set_flags(IFF_UP | IFF_RUNNING);
        }

        self.set_flags(-IFF_UP)
    }
}

impl Configurable for Device {
    fn from_configuration(configuration: &Configuration) -> Result<Self> {
        let dev_id = match configuration.name.as_ref() {
            Some(name) => {
                if name.len() > IFNAMSIZ {
                    return Err(ErrorKind::TunNameTooLong.into());
                }
                if !name.starts_with("utun") {
                    return Err(ErrorKind::InvalidTunName.into());
                }
                u8::from_str(&name["utun".len()..])? as u8
            }

            None => 0,
        };

        let tun = unsafe { socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL) };
        if tun < 0 {
            return Err(io::Error::last_os_error().into());
        }

        let mut info = ctl_info {
            ctl_id: 0,
            ctl_name: {
                let mut bufer = [0u8; 96];
                bufer[..UTUN_CONTROL_NAME.len()].clone_from_slice(UTUN_CONTROL_NAME.as_bytes());
                bufer
            },
        };

        unsafe {
            if ctliocginfo(tun, &mut info as *mut _ as *mut _) < 0 {
                close(tun);
                return Err(io::Error::last_os_error().into());
            }
        }

        let addr = sockaddr_ctl {
            sc_id: info.ctl_id,
            sc_len: mem::size_of::<sockaddr_ctl>() as u8,
            sc_family: AF_SYSTEM,
            ss_sysaddr: AF_SYS_CONTROL,
            sc_unit: dev_id as u32 + 1,
            sc_reserved: [0; 5],
        };

        if unsafe {
            connect(tun,
                    &addr as *const sockaddr_ctl as *const sockaddr,
                    mem::size_of_val(&addr) as socklen_t)
        } < 0
        {
            return Err(io::Error::last_os_error().into());
        }

        let mut name_buf = [0u8; 64];
        let mut name_length: socklen_t = 64;
        if unsafe {
            getsockopt(tun,
                       SYSPROTO_CONTROL,
                       UTUN_OPT_IFNAME,
                       &mut name_buf as *mut _ as *mut c_void,
                       &mut name_length as *mut socklen_t)
        } < 0
        {
            return Err(io::Error::last_os_error().into());
        }

        let ctl = unsafe { socket(AF_INET, SOCK_DGRAM, 0) };
        if ctl < 0 {
            return Err(io::Error::last_os_error().into());
        }

        let mut device = unsafe {
            Device {
                name: CStr::from_ptr(name_buf.as_ptr() as *const c_char).to_string_lossy().into(),
                tun: File::from_raw_fd(tun),
                ctl: File::from_raw_fd(ctl),
            }
        };

        device.configure(configuration)?;
        Ok(device)
    }
    fn configure(&mut self, configuration: &Configuration) -> Result<()> {
        if let Some(ip) = configuration.address {
            self.set_address(ip)?;
        }

        if let Some(ip) = configuration.destination {
            self.set_destination(ip)?;
        }

        if let Some(ip) = configuration.broadcast {
            self.set_broadcast(ip)?;
        }

        if let Some(ip) = configuration.netmask {
            self.set_netmask(ip)?;
        }

        if let Some(mtu) = configuration.mtu {
            self.set_mtu(mtu)?;
        }

        self.set_enabled(configuration.enabled)?;

        Ok(())
    }
}

include!("../unix/mio.rs.in");
<|endoftext|>


<commit_before>#![crate_name = "prob0043"]
#![crate_type = "rlib"]

extern crate math;

use std::fmt;
use std::iter::{AdditiveIterator, MultiplicativeIterator, Repeat};
use math::numconv;

pub static EXPECTED_ANSWER: &'static str = "16695334890";

#[deriving(Eq)]
struct DigitMap { used: [ bool, ..10] }

impl PartialEq for DigitMap {
    fn eq(&self, other: &DigitMap) -> bool { self.used == other.used }
}

impl fmt::Show for DigitMap {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.used.as_slice().fmt(f)
    }
}

impl DigitMap {
    #[inline]
    fn new() -> DigitMap { DigitMap { used: [ false, ..10 ] } }

    #[inline]
    fn is_used(&self, n: uint) -> bool {
        assert!(n < 10);
        self.used[n]
    }

    #[inline]
    fn join(&self, d: uint) -> Option<DigitMap> {
        assert!(d < 10);

        if self.is_used(d) { return None }

        let mut new_dm = *self;
        new_dm.used[d] = true;
        Some(new_dm)
    }

    #[inline]
    fn join_all<T: Iterator<uint>>(&self, mut ds: T) -> Option<DigitMap> {
        let mut dm = *self;
        for d in ds {
            match dm.join(d) {
                None    => return None,
                Some(x) => dm = x
            }
        }
        Some(dm)
    }
}

fn filled_digits(n: uint, radix: uint, len: uint) -> Vec<uint> {
    numconv::to_digits(n, radix).chain(Repeat::new(0u)).take(len).collect()
}

fn create_dm_list(base: uint, radix: uint, len: uint) -> Vec<(Vec<uint>, DigitMap)> {
    assert!(len > 0);
    let max = Repeat::new(radix).take(len).product();
    range(0, max / base)
        .filter_map(|n| {
            let ds = filled_digits(n * base, radix, len);
            match DigitMap::new().join_all(ds.iter().map(|&x| x)) {
                Some(dm) => Some((ds, dm)),
                None     => None
            }
        }).collect()
}

fn update_dm_list(list: &[(Vec<uint>, DigitMap)], base: uint, radix: uint, len: uint)
                  -> Vec<(Vec<uint>, DigitMap)>
{
    assert!(len > 0);
    let ord = Repeat::new(radix).take(len - 1).product();

    let mut result = Vec::new();
    for &(ref ds, ref dm) in list.iter() {
        let lower = numconv::from_digits(ds.slice(ds.len() - (len - 1), ds.len()), radix);
        let it = range(0, radix)
            .filter(|d| (d * ord + lower) % base == 0)
            .filter_map(|d| dm.join(d).map(|dm| (*ds + &[d], dm)));
        result.extend(it);
    }
    result
}

pub fn solve() -> String {
    let mut result = create_dm_list(17, 10, 3);
    let base_list = [13u, 11, 7, 5, 3, 2, 1];
    for &base in base_list.iter() {
        result = update_dm_list(result.as_slice(), base, 10, 3);
    }

    result.move_iter()
        .map(|(r, _e)| numconv::from_digits(r.as_slice(), 10))
        .sum()
        .to_string()
}

#[cfg(test)]
mod tests {
    mod digit_map {
        use super::super::DigitMap;

        #[test]
        fn join_with() {
            let dm = DigitMap::new().join(1).unwrap();

            assert_eq!(false, dm.is_used(0));
            assert_eq!(true, dm.is_used(1));
            assert_eq!(false, dm.is_used(2));
            assert_eq!(false, dm.is_used(4));

            assert_eq!(None, dm.join(1));

            let dm = dm.join(4).unwrap();
            assert_eq!(false, dm.is_used(0));
            assert_eq!(true, dm.is_used(1));
            assert_eq!(true, dm.is_used(4));
            assert_eq!(false, dm.is_used(7));
        }

        #[test]
        fn join_all() {
            let dm = DigitMap::new().join_all(vec![1u, 2, 3].move_iter()).unwrap();

            assert_eq!(false, dm.is_used(0));
            assert_eq!(true, dm.is_used(1));
            assert_eq!(true, dm.is_used(2));
            assert_eq!(false, dm.is_used(4));

            assert_eq!(None, DigitMap::new().join_all(vec![0u, 0].move_iter()));
        }

    }

    #[test]
    fn filled_digits() {
        assert_eq!(vec![], super::filled_digits(123, 10, 0));
        assert_eq!(vec![3], super::filled_digits(123, 10, 1));
        assert_eq!(vec![3, 2], super::filled_digits(123, 10, 2));
        assert_eq!(vec![3, 2, 1], super::filled_digits(123, 10, 3));
        assert_eq!(vec![3, 2, 1, 0], super::filled_digits(123, 10, 4));
    }

    #[test]
    fn create_dm_list() {
        let dm = super::create_dm_list(9, 10, 2);
        assert_eq!(10, dm.len());
        assert_eq!(vec![9, 0], *dm[0].ref0());
        assert_eq!(vec![8, 1], *dm[1].ref0());
        assert_eq!(vec![7, 2], *dm[2].ref0());
        assert_eq!(vec![6, 3], *dm[3].ref0());
        assert_eq!(vec![5, 4], *dm[4].ref0());
        assert_eq!(vec![4, 5], *dm[5].ref0());
        assert_eq!(vec![3, 6], *dm[6].ref0());
        assert_eq!(vec![2, 7], *dm[7].ref0());
        assert_eq!(vec![1, 8], *dm[8].ref0());
        assert_eq!(vec![0, 9], *dm[9].ref0());

        assert_eq!(0, super::create_dm_list(11, 10, 2).len());
    }

    #[test]
    fn update_dm_list() {
        let dm = super::update_dm_list(super::create_dm_list(9, 10, 2).as_slice(), 5, 10, 2);
        assert_eq!(16, dm.len());
        assert_eq!(vec![9, 0, 1], *dm[0].ref0());
        assert_eq!(vec![9, 0, 2], *dm[1].ref0());
        // snip
        assert_eq!(vec![9, 0, 8], *dm[7].ref0());
        assert_eq!(vec![4, 5, 0], *dm[8].ref0());
        assert_eq!(vec![4, 5, 1], *dm[9].ref0());
        assert_eq!(vec![4, 5, 2], *dm[10].ref0());
        assert_eq!(vec![4, 5, 3], *dm[11].ref0());
        assert_eq!(vec![4, 5, 6], *dm[12].ref0());
        assert_eq!(vec![4, 5, 7], *dm[13].ref0());
        assert_eq!(vec![4, 5, 8], *dm[14].ref0());
        assert_eq!(vec![4, 5, 9], *dm[15].ref0());
    }
}
<commit_msg>fix for compile<commit_after>#![crate_name = "prob0043"]
#![crate_type = "rlib"]

extern crate math;

use std::fmt;
use std::iter::{AdditiveIterator, MultiplicativeIterator, Repeat};
use math::numconv;

pub static EXPECTED_ANSWER: &'static str = "16695334890";

#[deriving(Eq)]
struct DigitMap { used: [ bool, ..10] }

impl PartialEq for DigitMap {
    fn eq(&self, other: &DigitMap) -> bool { self.used == other.used }
}

impl fmt::Show for DigitMap {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.used.as_slice().fmt(f)
    }
}

impl DigitMap {
    #[inline]
    fn new() -> DigitMap { DigitMap { used: [ false, ..10 ] } }

    #[inline]
    fn is_used(&self, n: uint) -> bool {
        assert!(n < 10);
        self.used[n]
    }

    #[inline]
    fn join(&self, d: uint) -> Option<DigitMap> {
        assert!(d < 10);

        if self.is_used(d) { return None }

        let mut new_dm = *self;
        new_dm.used[d] = true;
        Some(new_dm)
    }

    #[inline]
    fn join_all<T: Iterator<uint>>(&self, mut ds: T) -> Option<DigitMap> {
        let mut dm = *self;
        for d in ds {
            match dm.join(d) {
                None    => return None,
                Some(x) => dm = x
            }
        }
        Some(dm)
    }
}

fn filled_digits(n: uint, radix: uint, len: uint) -> Vec<uint> {
    numconv::to_digits(n, radix).chain(Repeat::new(0u)).take(len).collect()
}

fn create_dm_list(base: uint, radix: uint, len: uint) -> Vec<(Vec<uint>, DigitMap)> {
    assert!(len > 0);
    let max = Repeat::new(radix).take(len).product();
    range(0, max / base)
        .filter_map(|n| {
            let ds = filled_digits(n * base, radix, len);
            match DigitMap::new().join_all(ds.iter().map(|&x| x)) {
                Some(dm) => Some((ds, dm)),
                None     => None
            }
        }).collect()
}

fn update_dm_list(list: &[(Vec<uint>, DigitMap)], base: uint, radix: uint, len: uint)
                  -> Vec<(Vec<uint>, DigitMap)>
{
    assert!(len > 0);
    let ord = Repeat::new(radix).take(len - 1).product();

    let mut result = Vec::new();
    for &(ref ds, ref dm) in list.iter() {
        let lower = numconv::from_digits(ds.slice(ds.len() - (len - 1), ds.len()), radix);
        let it = range(0, radix)
            .filter(|d| (d * ord + lower) % base == 0)
            .filter_map(|d| {
                let sd: &[uint] = &[d];
                dm.join(d).map(|dm| (*ds + sd, dm))
            });
        result.extend(it);
    }
    result
}

pub fn solve() -> String {
    let mut result = create_dm_list(17, 10, 3);
    let base_list = [13u, 11, 7, 5, 3, 2, 1];
    for &base in base_list.iter() {
        result = update_dm_list(result.as_slice(), base, 10, 3);
    }

    result.move_iter()
        .map(|(r, _e)| numconv::from_digits(r.as_slice(), 10))
        .sum()
        .to_string()
}

#[cfg(test)]
mod tests {
    mod digit_map {
        use super::super::DigitMap;

        #[test]
        fn join_with() {
            let dm = DigitMap::new().join(1).unwrap();

            assert_eq!(false, dm.is_used(0));
            assert_eq!(true, dm.is_used(1));
            assert_eq!(false, dm.is_used(2));
            assert_eq!(false, dm.is_used(4));

            assert_eq!(None, dm.join(1));

            let dm = dm.join(4).unwrap();
            assert_eq!(false, dm.is_used(0));
            assert_eq!(true, dm.is_used(1));
            assert_eq!(true, dm.is_used(4));
            assert_eq!(false, dm.is_used(7));
        }

        #[test]
        fn join_all() {
            let dm = DigitMap::new().join_all(vec![1u, 2, 3].move_iter()).unwrap();

            assert_eq!(false, dm.is_used(0));
            assert_eq!(true, dm.is_used(1));
            assert_eq!(true, dm.is_used(2));
            assert_eq!(false, dm.is_used(4));

            assert_eq!(None, DigitMap::new().join_all(vec![0u, 0].move_iter()));
        }

    }

    #[test]
    fn filled_digits() {
        assert_eq!(vec![], super::filled_digits(123, 10, 0));
        assert_eq!(vec![3], super::filled_digits(123, 10, 1));
        assert_eq!(vec![3, 2], super::filled_digits(123, 10, 2));
        assert_eq!(vec![3, 2, 1], super::filled_digits(123, 10, 3));
        assert_eq!(vec![3, 2, 1, 0], super::filled_digits(123, 10, 4));
    }

    #[test]
    fn create_dm_list() {
        let dm = super::create_dm_list(9, 10, 2);
        assert_eq!(10, dm.len());
        assert_eq!(vec![9, 0], *dm[0].ref0());
        assert_eq!(vec![8, 1], *dm[1].ref0());
        assert_eq!(vec![7, 2], *dm[2].ref0());
        assert_eq!(vec![6, 3], *dm[3].ref0());
        assert_eq!(vec![5, 4], *dm[4].ref0());
        assert_eq!(vec![4, 5], *dm[5].ref0());
        assert_eq!(vec![3, 6], *dm[6].ref0());
        assert_eq!(vec![2, 7], *dm[7].ref0());
        assert_eq!(vec![1, 8], *dm[8].ref0());
        assert_eq!(vec![0, 9], *dm[9].ref0());

        assert_eq!(0, super::create_dm_list(11, 10, 2).len());
    }

    #[test]
    fn update_dm_list() {
        let dm = super::update_dm_list(super::create_dm_list(9, 10, 2).as_slice(), 5, 10, 2);
        assert_eq!(16, dm.len());
        assert_eq!(vec![9, 0, 1], *dm[0].ref0());
        assert_eq!(vec![9, 0, 2], *dm[1].ref0());
        // snip
        assert_eq!(vec![9, 0, 8], *dm[7].ref0());
        assert_eq!(vec![4, 5, 0], *dm[8].ref0());
        assert_eq!(vec![4, 5, 1], *dm[9].ref0());
        assert_eq!(vec![4, 5, 2], *dm[10].ref0());
        assert_eq!(vec![4, 5, 3], *dm[11].ref0());
        assert_eq!(vec![4, 5, 6], *dm[12].ref0());
        assert_eq!(vec![4, 5, 7], *dm[13].ref0());
        assert_eq!(vec![4, 5, 8], *dm[14].ref0());
        assert_eq!(vec![4, 5, 9], *dm[15].ref0());
    }
}
<|endoftext|>


<commit_before>#[crate_id = "prob0128"];
#[crate_type = "rlib"];

//! # 定義
//!
//! 最内のリングを 0 周目のリングとする。
//! `r` 周目のリングの要素数を `a(r)` 、
//! `r` 週目のリングの、真北から数えて `m` 番目のセルの数値を `b(r, m)` とそれぞれおくと、
//!
//! ```math
//! a(0) = 1
//! a(r) = 6r if r > 0
//!
//! b(0, 0) = 1
//! b(r, m) = \sum_{k=0}^{r-1} a(k) + 1 + m
//!         = 1 + 6 + 12 + ... + 6(r-1) + 1 + m
//!         = 2 + 3r(r-1) + m
//! ```
//!
//! となる。利便性のため、任意の `m` について、`b(r, m)` は以下を満たすとする。
//!
//! ```math
//! b(r, m) = b(r, m + a(r)) if m < 0 or m >= 6r
//! ```
//!
//! また、上記式より以下を得る。
//!
//! ```
//! b(r+1, m_a) - b(r, m_b) = 6r + m_a - m_b
//! b(r, m_a) - b(r, m_b)   = m_a - m_b
//! b(r, m_a) - b(r-1, m_b) = 6(r-1) + m_a - m_b
//! ```
//!
//! _r_ 周目のリング中のセルは、リングのどの部分に属しているかで以下のように分類する。
//!
//! * 角 - `m = rk` を満たすセル。 `r+1` 周目のセル3つと、`r-1` 周目のセル 1 つと隣接する。
//!        `r > 0` の周に存在。
//! * 辺 - 角ではないセル。`r+1` 周目のセルと、 `r-1` 周目のセルそれぞれに 2 つに隣接する。
//!        `r > 1` の周に存在。
//!
//! # 解析
//!
//! ## `r=0` の場合
//!
//! 周囲のセルの数値との差は `1`. `2`, `3`, `4`, `5`, `6` であり、
//! `2`, `3`, `5` が素数なので、 `PD(1) = 3` となる。
//!
//! ## 辺の場合
//!
//! `r` 周目の辺に属するセルの場合、`r+1` 周目の連続したセル2つおよび
//! `r-1` 週目の連続したセル2つと隣接する。
//!
//! ### `m \neq 6r-1` の場合
//
//! `m \neq 6r-1` の場合、連続した 2 つセルとの差 2 つは、いずれかは必ず偶数となる。
//! よって、これら 4 つのセルの数値との差のうち素数となるのは最大 2 つである。
//! すなわち、辺のセルについては `PD(n)` が 3 となることはない。
//!
//! ### `m = 6r-1` の場合
//!
//! `m = 6r-1` の場合、隣接するセルの数値との差は以下である。
//!
//! * `b(r+1, 6r+4) - b(r, 6r-1)   = 6r+5`
//! * `b(r+1, 6r+5) - b(r, 6r-1)   = 6(r+1)`
//! * `b(r, 6r-1)   - b(r, 6r-2)   = 1`
//! * `b(r, 6r-1)   - b(r, 0)      = 6r-1`
//! * `b(r, 6r-1)   - b(r-1, 0)    = 12r-7`
//! * `b(r, 6r-1)   - b(r-1, 6r-7) = 6r`
//!
//! `6(r+1)`, `1`, `6r` は素数ではないため、`PD(n) = 3` となるセルは、
//! `6r+5`, `6r-1`, `12r-7` が素数でなければならない。
//!
//! なお、 `b(r, 6r-1) = 3r^2 + 3r + 1` である。
//!
//! ## 角の場合
//!
//! 角のセルに隣接するセルの数値は、以下の6種類である。ここで、 `k = m/r` である。
//!
//! * `b(r+1, rk-1+k)`
//! * `b(r+1, rk+k)`
//! * `b(r+1, rk+1+k)`
//! * `b(r, rk-1)`
//! * `b(r, rk+1)`
//! * `b(r-1, rk-k)`
//!
//! ### `k > 0` の場合
//!
//! `k > 0` の場合、セルと隣接するセルの数値との差は以下である。
//!
//! * `b(r+1, rk-1+k) - b(r, rk)     = 6r + k - 1`
//! * `b(r+1, rk+k)   - b(r, rk)     = 6r + k`
//! * `b(r+1, rk+1+k) - b(r, rk)     = 6r + k + 1`
//! * `b(r, rk)       - b(r, rk-1)   = 1`
//! * `b(r, rk+1)     - b(r, rk)     = 1`
//! * `b(r, rk)       - b(r-1, rk-k) = 6(r-1) + k`
//!
//! `k` が偶数の場合、`6r+k`, `6(r-1)+k` は偶数となる。
//! `k` が奇数の場合、`6r+k-1`, `6r+k+1` は偶数となる。
//! よって、`k > 1` の場合、`PD(3)` となることはない。
//!
//! ### `k = 0` の場合
//!
//! `k = 0` の場合、セルと隣接するセルの数値との差は以下である。
//!
//! * `b(r+1, -1) - b(r, 0)   = b(r+1, 6r+5) - b(r, 0) = 12r + 5`
//! * `b(r+1, 0)  - b(r, 0)   = 6r`
//! * `b(r+1, 1)  - b(r, 0)   = 6r + 1`
//! * `b(r, -1)   - b(r, 0)   = b(r, 6r-1) - b(r, 0) = 6r - 1`
//! * `b(r, 1)    - b(r, 0)   = 1`
//! * `b(r, 0)    - b(r-1, 0) = 6r(r-1)`
//!
//! `6r`, `6r(r-1)`, `1` は素数ではないため、`PD(n) = 3` となるためには、
//! `12r+5`, `6r+1`, `6r-1` が素数でなければならない。
//!
//! なお、 `b(r, 0) = 3r^2 - 3r + 2` である。
//!
//! # 解法
//!
//! `r > 0` について、以下すべてが素数の場合、 `PD(n) = 3` となる。
//!
//! ```math
//! 12r+5, 6r+1, 6r-1
//! ```
//!
//! `r > 1` について、以下すべてが素数の場合、 `PD(n) = 3` となる。
//!
//! ```math
//! 6r-1, 6r-5, 12r-7
//! ```
//!
//! `r=0` から順番にこれらを満たす数をカウントする。

extern mod math;

use std::util;
use math::prime::Prime;

pub static EXPECTED_ANSWER: &'static str = "14516824220";

#[deriving(Eq)]
struct PdTriple {
    n: uint,
    r: uint,
    triple: (uint, uint, uint)
}

struct PdTriples {
    r: uint,
    next: Option<PdTriple>
}

impl PdTriples {
    #[inline]
    fn new() -> PdTriples { PdTriples { r: 0, next: None } }
}

impl Iterator<PdTriple> for PdTriples {
    #[inline]
    fn next(&mut self) -> Option<PdTriple> {
        if self.next.is_some() {
            return util::replace(&mut self.next, None)
        }

        let r = self.r;
        self.r += 1;

        if r == 0 { return Some(PdTriple { n: 1, r: 0, triple: (2, 3, 5) }) }
        if r > 1 {
            let n = 3*r*r + 3*r + 1;
            self.next = Some(PdTriple { n: n, r: r, triple: (6*r-1, 6*r+5, 12*r-7) });
        }
        let n = 3*r*r - 3*r + 2;
        Some(PdTriple { n: n, r: r, triple: (12*r+5, 6*r+1, 6*r-1) })
    }
}

struct Pd3Nums {
    iter: PdTriples,
    ps: Prime
}

impl Pd3Nums {
    #[inline]
    fn new() -> Pd3Nums { Pd3Nums { iter: PdTriples::new(), ps: Prime::new() } }
}

impl Iterator<uint> for Pd3Nums {
    #[inline]
    fn next(&mut self) -> Option<uint> {
        loop {
            let PdTriple { n, triple: (a, b, c), ..} = self.iter.next().unwrap();
            if self.ps.contains(a) && self.ps.contains(b) && self.ps.contains(c) {
                return Some(n)
            }
        }
    }
}

pub fn solve() -> ~str {
    Pd3Nums::new().nth(2000 - 1).unwrap().to_str()
}

#[cfg(test)]
mod test {
    use super::{PdTriple, PdTriples, Pd3Nums};
    use std::iter::AdditiveIterator;

    fn a(r: uint) -> uint { if r == 0 { 1 } else { 6 * r } }
    fn b(r: uint, m: uint) -> uint {
        if r == 0 {
            assert_eq!(0, m);
            return 1
        }
        assert!(m < 6 * r);
        range(0, r).map(a).sum() + 1 + m
    }

    #[test]
    fn test_a() {
        assert_eq!(1, a(0));
        assert_eq!(6, a(1));
        assert_eq!(12, a(2));
    }

    #[test]
    fn test_b() {
        assert_eq!(1, b(0, 0));
        let mut n = 2;
        for r in range(1u, 10) {
            for m in range(0u, a(r)) {
                assert_eq!(n, b(r, m));
                n += 1;
            }
        }
    }

    #[test]
    fn pd_triples() {
        let mut it = PdTriples::new();
        assert_eq!(Some(PdTriple { n: b(0, 0), r: 0, triple: (2u, 3u, 5u)}), it.next());
        let n = b(1, 0);
        assert_eq!(Some(PdTriple { n: n, r: 1, triple: (b(2, 11) - n,
                                                        b(2, 1)  - n,
                                                        b(1, 5)  - n)}),
                   it.next());

        for r in range(2u, 100) {
            let n = b(r, 0);
            assert_eq!(Some(PdTriple { n: n, r: r, triple: (b(r+1, 6*r+5) - n,
                                                            b(r+1, 1)     - n,
                                                            b(r,   6*r-1) - n) }),
                       it.next());

            let n = b(r, 6*r-1);
            assert_eq!(Some(PdTriple { n: n, r: r, triple: (n - b(r,0),
                                                            b(r+1, 6*r+4) - n,
                                                            n - b(r-1, 0))}),
                       it.next());
        }
    }

    #[test]
    fn pd3_nums() {
        let mut it = Pd3Nums::new();
        assert_eq!(Some(1), it.next());

        let mut it = Pd3Nums::new();
        assert_eq!(Some(271), it.nth(9));
    }
}
<commit_msg>Update doc<commit_after>//! Problem 128 (https://projecteuler.net/problem=128)
//!
//! # 定義
//!
//! 最内のリングを 0 周目のリングとする。
//! `r` 周目のリングの要素数を `a(r)` 、
//! `r` 週目のリングの、真北から数えて `m` 番目のセルの数値を `b(r, m)` とそれぞれおくと、
//!
//! ```math
//! a(0) = 1
//! a(r) = 6r if r > 0
//!
//! b(0, 0) = 1
//! b(r, m) = \sum_{k=0}^{r-1} a(k) + 1 + m
//!         = 1 + 6 + 12 + ... + 6(r-1) + 1 + m
//!         = 2 + 3r(r-1) + m
//! ```
//!
//! となる。利便性のため、任意の `m` について、`b(r, m)` は以下を満たすとする。
//!
//! ```math
//! b(r, m) = b(r, m + a(r)) if m < 0 or m >= 6r
//! ```
//!
//! また、上記式より以下を得る。
//!
//! ```
//! b(r+1, m_a) - b(r, m_b) = 6r + m_a - m_b
//! b(r, m_a) - b(r, m_b)   = m_a - m_b
//! b(r, m_a) - b(r-1, m_b) = 6(r-1) + m_a - m_b
//! ```
//!
//! _r_ 周目のリング中のセルは、リングのどの部分に属しているかで以下のように分類する。
//!
//! * 角 - `m = rk` を満たすセル。 `r+1` 周目のセル3つと、`r-1` 周目のセル 1 つと隣接する。
//!        `r > 0` の周に存在。
//! * 辺 - 角ではないセル。`r+1` 周目のセルと、 `r-1` 周目のセルそれぞれに 2 つに隣接する。
//!        `r > 1` の周に存在。
//!
//! # 解析
//!
//! ## `r=0` の場合
//!
//! 周囲のセルの数値との差は `1`. `2`, `3`, `4`, `5`, `6` であり、
//! `2`, `3`, `5` が素数なので、 `PD(1) = 3` となる。
//!
//! ## 辺の場合
//!
//! `r` 周目の辺に属するセルの場合、`r+1` 周目の連続したセル2つおよび
//! `r-1` 週目の連続したセル2つと隣接する。
//!
//! ### `m \neq 6r-1` の場合
//
//! `m \neq 6r-1` の場合、連続した 2 つセルとの差 2 つは、いずれかは必ず偶数となる。
//! よって、これら 4 つのセルの数値との差のうち素数となるのは最大 2 つである。
//! すなわち、辺のセルについては `PD(n)` が 3 となることはない。
//!
//! ### `m = 6r-1` の場合
//!
//! `m = 6r-1` の場合、隣接するセルの数値との差は以下である。
//!
//! * `b(r+1, 6r+4) - b(r, 6r-1)   = 6r+5`
//! * `b(r+1, 6r+5) - b(r, 6r-1)   = 6(r+1)`
//! * `b(r, 6r-1)   - b(r, 6r-2)   = 1`
//! * `b(r, 6r-1)   - b(r, 0)      = 6r-1`
//! * `b(r, 6r-1)   - b(r-1, 0)    = 12r-7`
//! * `b(r, 6r-1)   - b(r-1, 6r-7) = 6r`
//!
//! `6(r+1)`, `1`, `6r` は素数ではないため、`PD(n) = 3` となるセルは、
//! `6r+5`, `6r-1`, `12r-7` が素数でなければならない。
//!
//! なお、 `b(r, 6r-1) = 3r^2 + 3r + 1` である。
//!
//! ## 角の場合
//!
//! 角のセルに隣接するセルの数値は、以下の6種類である。ここで、 `k = m/r` である。
//!
//! * `b(r+1, rk-1+k)`
//! * `b(r+1, rk+k)`
//! * `b(r+1, rk+1+k)`
//! * `b(r, rk-1)`
//! * `b(r, rk+1)`
//! * `b(r-1, rk-k)`
//!
//! ### `k > 0` の場合
//!
//! `k > 0` の場合、セルと隣接するセルの数値との差は以下である。
//!
//! * `b(r+1, rk-1+k) - b(r, rk)     = 6r + k - 1`
//! * `b(r+1, rk+k)   - b(r, rk)     = 6r + k`
//! * `b(r+1, rk+1+k) - b(r, rk)     = 6r + k + 1`
//! * `b(r, rk)       - b(r, rk-1)   = 1`
//! * `b(r, rk+1)     - b(r, rk)     = 1`
//! * `b(r, rk)       - b(r-1, rk-k) = 6(r-1) + k`
//!
//! `k` が偶数の場合、`6r+k`, `6(r-1)+k` は偶数となる。
//! `k` が奇数の場合、`6r+k-1`, `6r+k+1` は偶数となる。
//! よって、`k > 1` の場合、`PD(3)` となることはない。
//!
//! ### `k = 0` の場合
//!
//! `k = 0` の場合、セルと隣接するセルの数値との差は以下である。
//!
//! * `b(r+1, -1) - b(r, 0)   = b(r+1, 6r+5) - b(r, 0) = 12r + 5`
//! * `b(r+1, 0)  - b(r, 0)   = 6r`
//! * `b(r+1, 1)  - b(r, 0)   = 6r + 1`
//! * `b(r, -1)   - b(r, 0)   = b(r, 6r-1) - b(r, 0) = 6r - 1`
//! * `b(r, 1)    - b(r, 0)   = 1`
//! * `b(r, 0)    - b(r-1, 0) = 6r(r-1)`
//!
//! `6r`, `6r(r-1)`, `1` は素数ではないため、`PD(n) = 3` となるためには、
//! `12r+5`, `6r+1`, `6r-1` が素数でなければならない。
//!
//! なお、 `b(r, 0) = 3r^2 - 3r + 2` である。
//!
//! # 解法
//!
//! `r > 0` について、以下すべてが素数の場合、 `PD(n) = 3` となる。
//!
//! ```math
//! 12r+5, 6r+1, 6r-1
//! ```
//!
//! `r > 1` について、以下すべてが素数の場合、 `PD(n) = 3` となる。
//!
//! ```math
//! 6r-1, 6r-5, 12r-7
//! ```
//!
//! `r=0` から順番にこれらを満たす数をカウントする。

#[crate_id = "prob0128"];
#[crate_type = "rlib"];

extern mod math;

use std::util;
use math::prime::Prime;

pub static EXPECTED_ANSWER: &'static str = "14516824220";

#[deriving(Eq)]
struct PdTriple {
    n: uint,
    r: uint,
    triple: (uint, uint, uint)
}

struct PdTriples {
    r: uint,
    next: Option<PdTriple>
}

impl PdTriples {
    #[inline]
    fn new() -> PdTriples { PdTriples { r: 0, next: None } }
}

impl Iterator<PdTriple> for PdTriples {
    #[inline]
    fn next(&mut self) -> Option<PdTriple> {
        if self.next.is_some() {
            return util::replace(&mut self.next, None)
        }

        let r = self.r;
        self.r += 1;

        if r == 0 { return Some(PdTriple { n: 1, r: 0, triple: (2, 3, 5) }) }
        if r > 1 {
            let n = 3*r*r + 3*r + 1;
            self.next = Some(PdTriple { n: n, r: r, triple: (6*r-1, 6*r+5, 12*r-7) });
        }
        let n = 3*r*r - 3*r + 2;
        Some(PdTriple { n: n, r: r, triple: (12*r+5, 6*r+1, 6*r-1) })
    }
}

struct Pd3Nums {
    iter: PdTriples,
    ps: Prime
}

impl Pd3Nums {
    #[inline]
    fn new() -> Pd3Nums { Pd3Nums { iter: PdTriples::new(), ps: Prime::new() } }
}

impl Iterator<uint> for Pd3Nums {
    #[inline]
    fn next(&mut self) -> Option<uint> {
        loop {
            let PdTriple { n, triple: (a, b, c), ..} = self.iter.next().unwrap();
            if self.ps.contains(a) && self.ps.contains(b) && self.ps.contains(c) {
                return Some(n)
            }
        }
    }
}

pub fn solve() -> ~str {
    Pd3Nums::new().nth(2000 - 1).unwrap().to_str()
}

#[cfg(test)]
mod test {
    use super::{PdTriple, PdTriples, Pd3Nums};
    use std::iter::AdditiveIterator;

    fn a(r: uint) -> uint { if r == 0 { 1 } else { 6 * r } }
    fn b(r: uint, m: uint) -> uint {
        if r == 0 {
            assert_eq!(0, m);
            return 1
        }
        assert!(m < 6 * r);
        range(0, r).map(a).sum() + 1 + m
    }

    #[test]
    fn test_a() {
        assert_eq!(1, a(0));
        assert_eq!(6, a(1));
        assert_eq!(12, a(2));
    }

    #[test]
    fn test_b() {
        assert_eq!(1, b(0, 0));
        let mut n = 2;
        for r in range(1u, 10) {
            for m in range(0u, a(r)) {
                assert_eq!(n, b(r, m));
                n += 1;
            }
        }
    }

    #[test]
    fn pd_triples() {
        let mut it = PdTriples::new();
        assert_eq!(Some(PdTriple { n: b(0, 0), r: 0, triple: (2u, 3u, 5u)}), it.next());
        let n = b(1, 0);
        assert_eq!(Some(PdTriple { n: n, r: 1, triple: (b(2, 11) - n,
                                                        b(2, 1)  - n,
                                                        b(1, 5)  - n)}),
                   it.next());

        for r in range(2u, 100) {
            let n = b(r, 0);
            assert_eq!(Some(PdTriple { n: n, r: r, triple: (b(r+1, 6*r+5) - n,
                                                            b(r+1, 1)     - n,
                                                            b(r,   6*r-1) - n) }),
                       it.next());

            let n = b(r, 6*r-1);
            assert_eq!(Some(PdTriple { n: n, r: r, triple: (n - b(r,0),
                                                            b(r+1, 6*r+4) - n,
                                                            n - b(r-1, 0))}),
                       it.next());
        }
    }

    #[test]
    fn pd3_nums() {
        let mut it = Pd3Nums::new();
        assert_eq!(Some(1), it.next());

        let mut it = Pd3Nums::new();
        assert_eq!(Some(271), it.nth(9));
    }
}
<|endoftext|>