Q:

Right Triangle Implementation

Given this struct:
struct RightTriangle {

var length: Double
var width: Double

var hypotenuse : Double {
    get {
        return sqrt((self.length * self.length) + (self.width * self.width))
    }
  }
}

I can easily create an instance of it:
var rightTriangleOne = RightTriangle(length: 3.0, width: 4.0)

and obtain the hypotenuse value:
rightTriangleOne.hypotenuse //returns 5.0

Now Here's the Problem
What if I want to set the hypotenuse to a specific value, and let the width and length values reflect the changes?
We could create a setter where we reassign the width or length (width in this case) based on the new hypotenuse value:
set {
      self.width = sqrt((newValue * newValue) - (length * length))
}

Now if we check our values:
rightTriangleOne.width // 4.0
rightTriangleOne.length // 3.0
rightTriangleOne.hypotenuse // 5.0

everything is good, but now let's try to set the hypotenuse to a new value:
rightTriangleOne.hypotenuse = 10.0 

Cool, let's now check our length and width (the width should modify itself to allow the hypotenuse to be 10, but not mess with the length):
rightTriangleOne.width // 9.539392014169456
rightTriangleOne.length // 3.0

This is great and all, but there is a problem. The problem is if we set the length or width to anything other than it's current value, the hypotenuse is going to change, and I don't want it to.
For example
If I set the hypotenuse to 10, and the width to 4, the length should be set to around 9.16.
Is this implementation possible? I'm trying to do this strictly with computed properties.
Thanks in advance.

A:

Use custom setters on width and length:
struct RightTriangle: CustomStringConvertible {
    private var _length: Double
    private var _width: Double

    var length: Double {
        get {
            return _length
        }
        set {
            let h = self.hypotenuse
            _length = newValue
            _width = sqrt(h * h - _length * _length)
        }
    }

    var width: Double  {
        get {
            return _width
        }
        set {
            let h = self.hypotenuse
            _width = newValue
            _length = sqrt(h * h - _width * _width)
        }

    }

    var hypotenuse: Double {
        get {
            return sqrt(length * length + width * width)
        }
        set {
            _width = sqrt((newValue * newValue) - (length * length))
        }
    }

    init(length: Double, width: Double) {
        _length = length
        _width = width
    }

    var description: String {
        return "length = \(length), width = \(width), hypotenuse = \(hypotenuse)"
    }
}

var x = RightTriangle(length: 3, width: 4)
x.hypotenuse = 10
x.width = 4

print(x) // length = 9.1651, width = 4.0, hypotenuse = 10.0

The ivars _length and _width are what actually stored the dimensions of the triangle. Their corresponding properties have custom setter to coordinate the change to the other side.
This comes with 2 assumptions:

If you change the hypotenuse, it's width that will be changed to match
It has no error check for impossible triangle dimensions, like negative, or length = 10, hypotenuse = 5 then width = nan

