Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
shodanjr_gr
Nov 20, 2007
Quick question on doing interval matching in a switch statement:

code:
switch diff {
        case 0.0:
		// no difference
        case -Double.infinity..<0.0:
            	// negative difference
        case 0.0...Double.infinity:
               // positive difference
        default:
            // error, shouldn't happen		
        }
Is there a way to implement this without the "cludgy" closed interval in the third case ([0.0 to Infinity]). The half-open range operator can only be half-open on the upper bound as far as I can tell and swapping the operands in order to get a "reverse range" is a no go.

Adbot
ADBOT LOVES YOU

ultramiraculous
Nov 12, 2003

"No..."
Grimey Drawer
Yeah I ran into that at some point. You can't make a half open interval where the start is not included, and you can't make one where the end is greater than the start (i.e. Double.infinity..<0.0). It seems like an <.. or a more relaxed HalfOpenInterval is what's needed.

Edit: Relatedly, I tried to make a <.. operator earlier and got stuck on this:

code:
// Expected '{' after operator name in 'operator' declaration
// Braced block of statements is an unused closure 
infix operator <.. {
    associativity none
    precedence 135
}
I completely ripped that off from the Swift headers, but I'm getting the sense that using "." in an operator might not exactly work?

ultramiraculous fucked around with this message at 15:47 on Jun 23, 2015

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Yes, . is restricted to only appear in a sequence, with ..< as a dumb special case.

Simulated
Sep 28, 2001
Lowtax giveth, and Lowtax taketh away.
College Slice
Amazingly (and as an awesome sign of the progress Swift has made) I just now ran into my first eternal loop in the compiler.

code:
let array = Array((0..<10000).map { (_) in Float(rand() % 10) })
let vecArray = stride(from: 0, to: array.count, by: 4).map { i in
    float4(array[i], array[i + 1], array[i + 2], array[i + 3])
}
Adding explicit type signatures to the map closure solves the problem. Opened rdar://21517757

toiletbrush
May 17, 2010
Is the idea of a 'strict' version of typealias (eg typealias Gram = Int and have the compiler complain if you try to pass an Int to a Gram parameter or add an Int to a Gram) a really bad one for an obvious dumb reason I haven't spotted? If not, is there any way to implement this in Swift, or would it be a possible future feature?

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.
Could use a struct.

jawbroken
Aug 13, 2007

messmate king
It would be nice if there was a way to “clone” the functionality of a struct, and then optionally extend it with new functionality, for cases like that. Then you wouldn't have to manually forward a bunch of methods to an underlying Int.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.
I suppose. Though a struct also lets you choose not to forward certain operations at your option. Dividing grams by grams may not make sense.

sarehu
Apr 20, 2007

(call/cc call/cc)
99% of the time when wrapping an Int you'll want equality operations and nothing else.

brap
Aug 23, 2004

Grimey Drawer
That makes me think it might be cool to have some kind of type system for units where the product of newtons and meters has the type newton-meters, etc. It would probably realistically have to be a piece of data attached to the value rather than the actual type of the value.

ljw1004
Jan 18, 2005

rum

fleshweasel posted:

That makes me think it might be cool to have some kind of type system for units where the product of newtons and meters has the type newton-meters, etc. It would probably realistically have to be a piece of data attached to the value rather than the actual type of the value.

"Units-of-measure" are part of the type system in the F# language, e.g.
code:
[<Measure>] type degC
[<Measure>] type degF
[<Measure>] type year

let temp = 15<degC>
let warming = 1<degC/year>
let temp_in_decade = temp + warming * 10<year>
printfn (if temp_in_decade < 92<degF> then "okay" else "hot")
This code gives an error on the comparison "The unit of measure 'degC' doesn't match 'degF'".

ultramiraculous
Nov 12, 2003

"No..."
Grimey Drawer
There was a kinda neat Scala library that did all of that too:

https://github.com/zzorn/ScalaQuantity

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
I solved this sort of problem in my own code with generics:

code:
public typealias UnitName = String
public protocol Unit {
    static var name: UnitName { get }
}

public struct Quantity<U: Unit>: Comparable {
    public let value: UInt
    
    public init(_ value: UInt) {
        self.value = value
    }
}

public func < <U> (lhs: Quantity<U>, rhs: Quantity<U>) -> Bool {
    return lhs.value < rhs.value
}

public func == <U> (lhs: Quantity<U>, rhs: Quantity<U>) -> Bool {
    return lhs.value == rhs.value
}
And usage, from my tests:

code:
    struct Cochrane: Unit {
        static var name: UnitName { return "Cochrane" }
    }
    
    struct Tesla: Unit {
        static var name: UnitName { return "Tesla" }
    }

    func testCanBeCompared() {
        let one = Quantity<Cochrane>(42)
        let two = Quantity<Tesla>(42)
        let three = Quantity<Cochrane>(12)
        let four = Quantity<Tesla>(42)
        
        XCTAssertTrue(one > three)
        // XCTAssertFalse(one == two) // Compiler error
        XCTAssertTrue(two == four)
    }
It's worked out pretty well for me. Means I have to go and define all my custom relationships, but it gives me a nice level of type safety I wouldn't otherwise have, so I put up with the semi-boilerplate. Using typealiases once I have the Quantity<U> really cuts down on it, too.

Simulated
Sep 28, 2001
Lowtax giveth, and Lowtax taketh away.
College Slice

You can extend this sort of thing quite easily:

code:
protocol DistanceConvertible: Unit {
    static var metersConversionFactor: Double { get }
}

struct Meters: Unit, DistanceConvertible {
    static var name: UnitName { return "Meters" }
    static var metersConversionFactor: Double { return 1.0 }
}
struct Kilometers: Unit, DistanceConvertible {
    static var name: UnitName { return "Kilometers" }
    static var metersConversionFactor: Double { return 1000.0 }
}
struct Miles: Unit, DistanceConvertible {
    static var name: UnitName { return "Miles" }
    static var metersConversionFactor: Double { return 1609.34 }
}

func + <U1:DistanceConvertible, U2:DistanceConvertible>
    (lhs: Quantity<U1>, rhs: Quantity<U2>) -> Quantity<Meters> {
        return Quantity(UInt(
            (Double(lhs.value) * U1.metersConversionFactor) +
                (Double(rhs.value) * U2.metersConversionFactor)))
}

let dist1 = Quantity<Meters>(5000)
let dist2 = Quantity<Miles>(1)
let result = dist1 + dist2
Then addition is only defined for Quantity where U is a Distance (leaving the question of using UInt vs fractional distances aside).

If you want to get fancier, you can allow arbitrary units on the result:

code:
extension Quantity where U:Distance {
    init(meters:UInt) {
        self.value = UInt(Double(meters) / U.metersConversionFactor)
    }
}

func + <U1:Distance, U2:Distance, U3:Distance>
    (lhs: Quantity<U1>, rhs: Quantity<U2>) -> Quantity<U3> {
        return Quantity(meters:UInt(
            (Double(lhs.value) * U1.metersConversionFactor) +
                (Double(rhs.value) * U2.metersConversionFactor)))
}

let iWouldWalk = Quantity<Miles>(500)
let thenIWouldWalk = Quantity<Meters>(804660)
let totalWalked: Quantity<Kilometers> = iWouldWalk + thenIWouldWalk

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

Ender.uNF posted:

more stuff

This is really awesome! I didn't end up doing anything like that because it's not something I much need to do; my stuff is mostly multiplication and division (and I intentionally chose UInts instead of Doubles for my purposes; I could very much see doing it Doubles if it suits your needs)

Because my game has an NSTimer firing a tick every so often, I went in the direction of also capturing a DeltaQuantity, so e.g. my "income increase per tick" is a DeltaQuantity<Dollar>, to expressly capture the act of the quantity changing. For whatever reason, it just bothers me to be able to do + and - directly on quantities (what does it mean to do 3 meters - 5 meters? -2 meters? What does that even mean in terms of measuring (as opposed to position)).

There's a lot of cool ways someone could take this if so inclined.

Carthag Tuek
Oct 15, 2005

Tider skal komme,
tider skal henrulle,
slægt skal følge slægters gang



Most of you have probably seen this newsletter, but for those who haven't:

https://swiftnews.curated.co

Doctor w-rw-rw-
Jun 24, 2008
NS*WindowLevel constants don't seem to be imported into Swift.

Is this an appropriate workaround?

Swift code:
window.level = Int(CGWindowLevelForKey(CGWindowLevelKey.FloatingWindowLevelKey))
(CGWindowLevelForKey returns an Int32; window.level expects an Int)

EDIT: reported as rdar://21640539

Doctor w-rw-rw- fucked around with this message at 22:46 on Jul 1, 2015

Doctor w-rw-rw-
Jun 24, 2008
Possibly dumb question:

code:
// Obviously incomplete implementation
extension CollectionType where Generator.Element: Equatable {
    func delta(otherSequence: Self) -> Delta {
        for (otherIndex, otherItem) in otherSequence.enumerate() {
            let foo = self[otherIndex] // Errors with: "Cannot subscript a value of type 'Self' with an index of type 'Int'"
        }
        return Delta()
    }
}
What am I doing wrong here? Why can't I subscript lowercase-s 'self' when CollectionType's protocol is explicitly extended with "subscript (position: Self.Index) -> Self.Generator.Element { get }"? Index(otherIndex) also doesn't work.

Kallikrates
Jul 7, 2002
Pro Lurker
(looking at 1.2 docs)
enumerate() returns the tuple of n and item where n is always an Int (running index of the enumeration)

not all collections are indexed by Ints?

Doctor w-rw-rw-
Jun 24, 2008
Well, this errors with: Type 'Index' constrained to non-protocol type 'Int'

code:
extension CollectionType where Index : Int, Generator.Element: Equatable {
    func delta(otherSequence: Self) -> Delta {
        for (otherIndex, otherItem) in otherSequence.enumerate() {
            let foo = self[otherIndex]
        }

        return Delta()
    }
}

Flobbster
Feb 17, 2005

"Cadet Kirk, after the way you cheated on the Kobayashi Maru test I oughta punch you in tha face!"

Doctor w-rw-rw- posted:

Well, this errors with: Type 'Index' constrained to non-protocol type 'Int'

code:
extension CollectionType where Index : Int, Generator.Element: Equatable {
    func delta(otherSequence: Self) -> Delta {
        for (otherIndex, otherItem) in otherSequence.enumerate() {
            let foo = self[otherIndex]
        }

        return Delta()
    }
}

Since Int isn't a protocol (nor is it inheritable), you need to say where Index == Int here (assuming Swift 2... I think Swift 1 uses single =).

Which begs the question, what's the difference between "where X == Y" and "where X : Y" if Y is a protocol? The compiler seems to allow both.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
One is a constraint that the type has to conform to the protocol, the other is a constraint that the type has to literally be that protocol type.

In theory, what you're looking for is:
Swift code:
extension CollectionType {
  func delta(otherSequence: Self) -> Delta {
    for otherIndex in otherSequence.indices {
      let mine = self[otherIndex]
      let other = otherSequence[otherIndex]
    }
  }
}
However, indexes are really bound to a specific collection, so this is actually not nearly as generic as you might think it is; even constraining the index type to be Int doesn't quite save you, because sequences can have different lengths and Int indexes aren't necessarily zero-bounded. Maybe you should just extend Array unless you have a need to be more generic?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Have I explained protocol types in this thread? The distinction between them and protocols is both interesting (I think) and important; it's probably discussed in the book, if you haven't checked it out.

Flobbster
Feb 17, 2005

"Cadet Kirk, after the way you cheated on the Kobayashi Maru test I oughta punch you in tha face!"

rjmccall posted:

Have I explained protocol types in this thread? The distinction between them and protocols is both interesting (I think) and important; it's probably discussed in the book, if you haven't checked it out.

Can you elaborate on the distinction? I searched through the book but couldn't find anything that about "protocol types" that seemed like anything outside the normal realm of protocols that I'm already familiar with. (I may have missed it.)

Doctor w-rw-rw-
Jun 24, 2008

rjmccall posted:

Maybe you should just extend Array unless you have a need to be more generic?
Nope, don't need to be more generic. I was just trying to learn what the most appropriate extension point was. My reasoning for trying this first before going for the "boring" solution was that I figured that if there's ever an ordered set, it probably wouldn't subclass Array, so I should check my mileage on the more generic solutions first.

Thanks!

eschaton
Mar 7, 2007

Don't you just hate when you wind up in a store with people who are in a socioeconomic class that is pretty obviously about two levels lower than your own?

fleshweasel posted:

code:

if let regex = NSRegularExpression(pattern: "\\d\\d",
    options: NSRegularExpressionOptions.allZeros, error: errorPtr) {
        if (errorPtr != nil) {

You shouldn't be checking errorPtr here, as with everything that deals with NSError you'll only get one back if the init fails and that's what you need to check for in Swift pre-2.0 or ObjC. (I can't tell you how many times I've seen [managedObjectContext save:&error]; if (error) { ... }, whoever told people they should do that is wrong wrong wrong.)

Kallikrates
Jul 7, 2002
Pro Lurker
That's the convention and I know in this case it isn't true but there are many older Apple API that don't follow it. It's simplistic to say that you should never check if error is set to see if an error occurred.
I could be mis remembering or there could be more modern methods but there are some signatures that return void and accept the error pointer.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

Kallikrates posted:

It's simplistic to say that you should never check if error is set to see if an error occurred.

Good thing that's not the rule! The rule is "check the return value to see if an error occurred". Which is exactly as simple as it should be.

Kallikrates
Jul 7, 2002
Pro Lurker
Okay I wrote that before coffee, I've run across methods that accept an error pointer, and return void. Or maybe I am mis remembering but I remember hitting something from some big Framework that was essentially: -(void)foo:(NSError **)error;

Doctor w-rw-rw-
Jun 24, 2008
Why does this succeed:

code:
extension Array {
    func delta<T: Equatable>(otherSequence: [T]) -> Delta {
        var unchangedIndices : [Int] = []

        for (otherIndex, otherItem) in otherSequence.enumerate() {
            let item = self[otherIndex] as! T
            if (item == otherItem) {
                unchangedIndices.append(otherIndex)
            }
        }

        // ...

        return Delta(unchangedIndices: unchangedIndices)
    }
}
and this fail?
code:
        // ... same

        for (otherIndex, otherItem) in otherSequence.enumerate() {
            let item = self[otherIndex]
            if (item == otherItem) {
                unchangedIndices.append(otherIndex)
            }
        }

        // ... same
Xcode already tells me item is of type T, but adding as! T prevents "Could not find an overload for '==' that accepts the supplied arguments".

EDIT: I think I'm dumb and need to use Element, not T.
EDIT 2: Nope. "'T' is not convertible to 'Element'" for "let item : Element = self[otherIndex]"
EDIT 3: Okay, using 'T' as a name is foolish, because it collides. When you're being told "'T' is not convertible to 'T'" best to use a different parameter name.
EDIT 4: All I really want is to define an operation (for calculating array diffs) on an Array where the elements are equatable. What the hell am I doing wrong?! :(

Doctor w-rw-rw- fucked around with this message at 18:23 on Jul 2, 2015

Flobbster
Feb 17, 2005

"Cadet Kirk, after the way you cheated on the Kobayashi Maru test I oughta punch you in tha face!"
I'm working on a library that is starting to push the sane limits of what generics and protocols can do in Swift, and I'm running into some weird situations where I feel like what I'm doing is going to bite me in the rear end later.

First, I've defined a protocol FloatingPointArithmetical that just defines all the standard arithmetic operators and math functions (sin, cos, sqrt, etc), and I have Double, Float, and CGFloat all extended to conform to those. Straight forward. (It would be great if the standard library already did this in FloatingPointType!)

Then, let's say I have a struct Foo<T> that contains an array of T and some other state and methods, and I have a likewise-defined protocol with an associated type:

code:
public protocol FooProtocol {
  typealias Value
  // other stuff
}
public struct Foo<T>: FooProtocol {
  typealias Value = T
  private var bar: [T]
  private func manglePrivates() { /* ... */ }
}
I want to extend Foo with additional methods only when T conforms to FloatingPointArithmetical. The catch is, my extension methods need to be able to call the private manglePrivates method inside Foo.

My first thought was to extend the struct with a where clause (extension struct Foo where Value: FloatingPointArithmetical) but that doesn't work because you can only extend protocols with where-clauses. Fair enough, I can extend FooProtocol instead, but that would require me to lift up any private data from Foo as vars/funcs in FooProtocol so that my extension methods can see it. I don't want to expose that.

I thought, what if I created a private _FooProtocol that defined manglePrivates, and had FooProtocol inherit from that? Can't do it—a public protocol can't inherit from a less-accessible protocol.

It turns out what I can do is this:

code:
private protocol _FooProtocol {
  func manglePrivates()
}
public protocol FooProtocol { /* same as above */ }

public struct Foo<T>: FooProtocol, _FooProtocol { /* same as above */ }

public extension FooProtocol where Value: FloatingPointArithmetical, Self: _FooProtocol {
  public func myFloatingPointMethod() {
     manglePrivates()
  }
}

let x = Foo<String>()  // x doesn't have myFloatingPointMethod, which is correct
let y = Foo<Double>()
y.myFloatingPointMethod()  // also correct
:stare:

It compiles and runs, but I look at that and see two things that make me a little worried:

1. I've got a struct extending a private protocol; why is this allowed but it's not for protocol inheritance?
2. I've got that bizarre Self constraint in my extension to fake a composed protocol extension (I tried extending protocol<FooProtocol, _FooProtocol> but non-nominal types can't be extended, and I couldn't define my own named protocol that inherits from both because of the same access control issue above).

Am I going to regret this?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Flobbster posted:

Can you elaborate on the distinction? I searched through the book but couldn't find anything that about "protocol types" that seemed like anything outside the normal realm of protocols that I'm already familiar with. (I may have missed it.)

A protocol is a set of requirements: methods, operators, associated types, implied protocol requirements, etc. It can be used in three ways in the language: in a conformance, in a generic requirement, and as a type.

Conformances are straightforward: you can declare that a type conforms to a protocol, in which case the compiler checks that the requirements are satisfied and registers the conformance.

Generic requirements are also simple: if you have generic code, you can declare that one of your type parameters (or an associated type thereof) has to conform to a protocol, in which case the generic code can now assume that conformance, but uses of it will have to check that the conformance exists.

The third use is as a type, or as part of a protocol composition type (protocol<A,B,C>). A value of a protocol (composition) type is a value of an unknown type that conforms to the protocol(s). In type theory, this is what's called an existential type, because one way you can interpret the type MyProto is ∃ t : MyProto . t. When you create a value of the protocol type from a value of a concrete type, you're erasing information about the concrete type (from the static type system; you can dynamically cast a value of protocol type back out to the concrete type); the compiler just checks that the concrete type conforms to the protocol. If you have a variable of protocol type, reassignment can change the concrete type that's stored in the variable.

There are some limits on what you can do with protocol types. Consider calling a method on a value of a protocol type. You know that the value has some type T that conforms to the protocol; but what you can't know (or at least the compiler can't know) is how that type is related to the type of any other given value, or the type stored in another value of the protocol type. So, for example, a protocol type for the protocol Equatable just wouldn't work, because you'd never be able to use the equality operator, because you'd never actually know that the types are the same. (Now, equality is something of a special case, because it's tempting to say "well, just say that they're not equal if the types aren't the same". But that's not a general answer; consider what it would do to the inequality operator.)

Anyway, that's the difference between protocols and protocol types.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Doctor w-rw-rw- posted:

Xcode already tells me item is of type T, but adding as! T prevents "Could not find an overload for '==' that accepts the supplied arguments".

EDIT: I think I'm dumb and need to use Element, not T.
EDIT 2: Nope. "'T' is not convertible to 'Element'" for "let item : Element = self[otherIndex]"
EDIT 3: Okay, using 'T' as a name is foolish, because it collides. When you're being told "'T' is not convertible to 'T'" best to use a different parameter name.
EDIT 4: All I really want is to define an operation (for calculating array diffs) on an Array where the elements are equatable. What the hell am I doing wrong?! :(

code:
extension Array where Element: Equatable {

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Flobbster posted:

I want to extend Foo with additional methods only when T conforms to FloatingPointArithmetical. The catch is, my extension methods need to be able to call the private manglePrivates method inside Foo.

My first thought was to extend the struct with a where clause (extension struct Foo where Value: FloatingPointArithmetical) but that doesn't work because you can only extend protocols with where-clauses.

That was fixed in Beta 2.

I'm going to hope that that works well enough for you and ignore the rest of your question. :)

Flobbster
Feb 17, 2005

"Cadet Kirk, after the way you cheated on the Kobayashi Maru test I oughta punch you in tha face!"

rjmccall posted:

That was fixed in Beta 2.

I'm going to hope that that works well enough for you and ignore the rest of your question. :)

That'll teach me to fall behind on my betas :) Thanks, upgrading to beta 2 did the trick and simplified my code quite a bit!

The only hang-up I ran into that had me scratching my head for a minute: my extension's constraint had to be applied to the generic type parameter name T and not to the protocol's typealias Value; trying to do the latter (a holdover from the protocol-based extension) caused the compiler to segfault. I filed rdar://21659734 for this.

Thanks for the write-up about protocols vs. protocol types. Clearly I wish my graduate PL course had been more theoretical instead of a survey of languages :v: But that definitely makes sense in the way that protocols with Self requirements can't be used as types in the context of variable decls and whatnot.

eschaton
Mar 7, 2007

Don't you just hate when you wind up in a store with people who are in a socioeconomic class that is pretty obviously about two levels lower than your own?

Kallikrates posted:

Okay I wrote that before coffee, I've run across methods that accept an error pointer, and return void. Or maybe I am mis remembering but I remember hitting something from some big Framework that was essentially: -(void)foo:(NSError **)error;

None that I’ve seen in the Cocoa APIs are like that. It wouldn’t surprise me though if there were some developers who misunderstood the conventions and made frameworks like that.

There are places in Cocoa where an error itself is passed as a parameter, and nil is used to indicate “no error.” But that's different then checking whether an error out-parameter was set.

Doctor w-rw-rw-
Jun 24, 2008

Flobbster posted:

That'll teach me to fall behind on my betas :)

Same. I was trying stuff, not realizing I was on an old Xcode 7.0 beta.

Doctor w-rw-rw-
Jun 24, 2008


Both in the project, and even in a barebones playground (rdar://21673413). Huh.

(Yes, I've changed Element to T, and it's fine.)

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Oh, sorry, I should have checked on the actual beta. (We're renaming generic arguments to be more meaningful.)

Adbot
ADBOT LOVES YOU

Flobbster
Feb 17, 2005

"Cadet Kirk, after the way you cheated on the Kobayashi Maru test I oughta punch you in tha face!"
I was excited yesterday to find a situation where a particular combination of property declarations in a harmless looking generic struct compiled fine, but deadlocked at runtime when I tried initializing the struct (rdar://21664816) :v:

code:
public struct Foo<T> {
  public var thing1: T?
  public var thing2: (Foo -> Void)?
}

let foo = Foo<String>(); // nope nope nope
Will we one day be able to add stored properties to extensions, pretty please? If this was Objective-C I would just use associated objects, but I live in a Swift-only structful world whenever possible. Since I've been writing these things where certain structs have additional methods based on their associated types, there are some cases where it would be nice for them to have their own data too.

Storage and memory layout requirements aside, I guess one big blocker would be that initializers on the main struct wouldn't be able to initialize properties in an extension, so non-optional things would be broken.

Flobbster fucked around with this message at 17:17 on Jul 4, 2015

  • Locked thread