Bronze/Gold/Platinum Challenges: Ch. 25

Bronze Challenge:

Easy enough. Implement the functionality of the + operator to Point:

struct Point: Comparable {
    ...
    static func +(lhs: Point, rhs: Point) -> Point {
        return Point(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
}

Gold Challenge: I wasn’t able to get this one to stick in my head. Any help is appreciated!

Platinum Challenge

First, I wanted to create a function to calculate the Euclidean Distance for a point (passing in the point you want to calculate the distance from, in case you want to pick something different than the origin (print statement for debugging purposes). Ths also included defining a new operator that could handle exponents in the function (found the solution for the ^^ operator online, also sets precedence so things are calculated correctly):

/*
 * creating the ^^ operator to use later in Euclidean Distance func.
 * Also needs to be outside the struct, as it must be a file-level operator.
 */
precedencegroup ExponentiativePrecedence {
    associativity: right
    higherThan: MultiplicationPrecedence
}

infix operator ^^: ExponentiativePrecedence
public func ^^ (radix: Double, power: Double) -> Double {
    return pow((radix), (power))
}

struct Point: Comparable {
    ...
    func euclideanDistance(to other: Point) -> Double {
        let distance = sqrt((Double(self.x) - Double(other.x))^^2 + (Double(self.y) - Double(other.y))^^2)
        print("Calculated distance for (\(self.x), \(self.y)) to origin: \(distance)")
        return distance
    }
}

Then, you can use that in the < operator definition to calculate the distance for each point passed in, and determine whether or not one is equal to the other:

struct Point: Comparable {
    ...
    static func <(lhs: Point, rhs: Point) -> Bool {
        let originPoint = Point(x: 0, y: 0)
        let lhsDistance = originPoint.euclideanDistance(to: lhs)
        let rhsDistance = originPoint.euclideanDistance(to: rhs)
        
        return lhsDistance < rhsDistance
    }
    ...
}

Now it works as expected.

Note for authors: learning about precedence at the end of the chapter would’ve been very helpful in one of the previous challenges when we were working on the lexer/parsing engine and had to figure out how to set precedence for multiplcation over addition and such (although I understand why it was mentioned in this chapter since this was focused on custom operators). Just pointing that out!

@macintacos, here is my solution for the Gold challenge. I was confused because after creating my people array, whenever I was typing “people.index(of:” I wasn’t getting any autocomplete. So, I just finished typing it in, “var p1Index = people.index(of:p1)”, and then I understood what the Gold challenge was asking us to do.

Essentially, it is asking us to use the index(of:) to find the index of the people array, but we have to do something to get rid of the error that comes when we just try to use index(of:) on the people array. The issue is that the class is not Equatable. Since it’s not Equatable, the index(of:) method, which finds the index of the element (by checking equality), doesn’t know how to determine index(of:). The error message is “error: cannot invoke ‘index’ with an argument list of type ‘(of: Person)’”. The solution is to make Person Equatable:

class Person: Equatable {
var name: String
var age: Int

init(name: String, age: Int) {
    self.name = name
    self.age = age
}

static func ==(p1: Person, p2: Person) -> Bool {
   return (p1.name == p2.name) && (p2.age == p1.age)
}

}

var p1 = Person(name: "David", age: 39)
var p2 = Person(name: "Ricardo", age: 42)
var people = [p1, p2]

var p1Index = people.index(of:p1) // gives 0, as expected

For the Gold challenge:
To distinguish two reference instances, better to compare addresses.

class Person: Equatable {
  let name: String
  let age: Int
  
  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }
  
  static func ==(p1: Person, p2: Person) -> Bool {
    return p1 === p2    // Compare addresses, not values
  }
}

let p1 = Person(name: "Alex", age: 18)
let p2 = Person(name: "Ann", age: 16)
let p3 = Person(name: "Alex", age: 18)    // p3 has same content as p1
let people = [p1, p2, p3]

let personIndex = people.index(of: p3)  // 2
1 Like

For the Platinum Challenge:
Honestly, the Gold Challenge is harder than this one.

struct Point: Comparable {
  let x: Int
  let y: Int
  
  static func ==(lhs: Point, rhs: Point) -> Bool {
    return (lhs.x == rhs.x) && (lhs.y == rhs.y)
  }
  
  var euclideanDistanceFromOrigin: Double {
    return sqrt(Double(x * x + y * y))
  }
  
  static func <(lhs: Point, rhs: Point) -> Bool {
    return lhs.euclideanDistanceFromOrigin < rhs.euclideanDistanceFromOrigin
  }
}

let c = Point(x: 3, y: 4)
let d = Point(x: 2, y: 5)

let cGreaterThanD = (c > d) // false
let cLessThanD = (c < d)    // true
let cEqualToD = (c == d)    // false
1 Like

Could you, please, write the page, on which the comparison of addresses is described, I can’t find it?

And could you explain this part of your code in platinum challenge?

(Double(x * x + y * y))

Chapter 3 Conditionals

if/else

Table 3.1 Comparison operators

===
Evaluates whether the two instances point to the same reference.

Both x and y are of type integers (Int), The result of multiplication and addition on integers is also an integer. However, the function sqrt() (square root) which returns Double operates on parameter of type Double. So we need to cast the integer (x * x + y * y) to Double with Double()

I think you should use the euclidean distance in the operator ‘==’ too.
With the current definition (comparison of x and y of both sides), we have this strange result:

let pa = Point(x: 5, y: 3)
let pb = Point(x: 3, y: 5)

print(pa == pb)  // false (although distance of pa and pb from the origin is the same)

Then we have also:

print(pa < pb)   // false
print(pa > pb)   // false

Whereas the goal of the platinium challenge was precisely to avoid this situation.

@Ruben, you are correct.

The == operator is updated accordingly.

static func ==(lhs: Point, rhs: Point) -> Bool {
//    return (lhs.x == rhs.x) && (lhs.y == rhs.y)
  return lhs.euclideanDistanceFromOrigin == rhs.euclideanDistanceFromOrigin
}

Hello. This is my solution for platinum challenge:

``struct Point: Equatable, Comparable {
let x, y: Int

static func ==(lhs: Point, rhs: Point) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y
}
static func <(lhs: Point, rhs: Point) -> Bool {
    return sqrt(Double(lhs.x * lhs.x + lhs.y * lhs.y)) < sqrt(Double(rhs.x * rhs.x + rhs.y * rhs.y))
}

}``

Here are my solutions to the Bronze and Platinum challenges (I used some help from this page):

import Cocoa

struct Point: Comparable {
    let x: Int
    let y: Int
    
    // required to conform to Equatable protocol, from whcih Comparable inherits
    static func ==(lhs: Point, rhs: Point) -> Bool {
        return sqrt(Double((lhs.x * lhs.x) + (lhs.y * lhs.y))) == sqrt(Double((rhs.x * rhs.x) + (rhs.y * rhs.y)))
    }
    
    static func <(lhs: Point, rhs: Point) -> Bool {
        let lhsDistFromOrigin = sqrt(Double((lhs.x * lhs.x) + (lhs.y * lhs.y)))
        let rhsDistFromOrigin = sqrt(Double((rhs.x * rhs.x) + (rhs.y * rhs.y)))
        return lhsDistFromOrigin < rhsDistFromOrigin
    }
    
    static func +(lhs: Point, rhs: Point) -> Point {
        return Point(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
}

let a = Point(x: 4, y: 3)
let b = Point(x: 3, y: 4)
let abEqual = (a == b)
let abNotEqual = (a != b)

let c = Point(x: 0, y: 10)
let d = Point(x: 6, y: 8)

let cdEqual = ( c == d)
let cLessThanD = (c < d)

// the following functions are provided by default when conforming to Comparable (based on the implementation of the required '<' function)
let cLessThanEqualD = ( c <= d)
let cGreaterThanD = (c > d)
let cGreaterThanEqualD = (c >= d)

let cPlusD = c + d
print(cPlusD.x)
print(cPlusD.y)

This is what I got for the Gold Challenge.

class Person:Equatable {
let name: String
let age: Int

init(name: String, age: Int) {
    self.name = name
    self.age = age
}


 static func ==(lhs: Person, rhs: Person) -> Bool{
    return (lhs.name == rhs.name) && (lhs.age == rhs.age)
}

}

let p1 = Person(name: “Test User1”, age: 37)
let p2 = Person(name: “Test User2”, age: 45)
var people:[Person] = []
people.append(p1)
people.append(p2)
people.index(of: p1)

You have to conform to the equatable protocol to get access to index(of:)

I got the gold one to work by making Person conform to the Equatable protocol and implementing
a static function which returns true or false depending on String and Int comparisons of Point’s properties.

PLATINUM CHALLENGE

As @hkray, simply generalising the origin point:

struct Point: Comparable {
    var x: Double
    var y: Double

    // default implementation: return the euclidean distance to 0, 0 origin
    static func <(lhs: Point, rhs: Point) -> Bool {
        let origin = Point(x: 0, y: 0)
        return lhs.euclideanDistance(from: origin) <  rhs.euclideanDistance(from: origin)
    }

    static func ==(lhs: Point, rhs: Point) -> Bool {
        return (lhs.x == rhs.x) && (lhs.y == rhs.y)
    }

    static func +(lhs: Point, rhs: Point) -> Point {
        return Point(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
    
    func euclideanDistance(from origin: Point) -> Double {
        return sqrt(pow(x - origin.x, 2)  + pow(y - origin.y, 2))
    }
}