// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Rational number type.
///
/// Invariants:
/// - The denominator is always positive.
/// - The numerator and denominator are always coprime.
struct Rational[T] {
  numerator : T
  denominator : T
} derive(Eq)

///|
/// Returns the numerator of this rational number.
pub fn[T] Rational::numerator(self : Rational[T]) -> T {
  self.numerator
}

///|
/// Returns the denominator of this rational number.
pub fn[T] Rational::denominator(self : Rational[T]) -> T {
  self.denominator
}

///|
trait Integral: Add + Sub + Mul + Div + Neg + Mod + Show + Eq + Compare + @quickcheck.Arbitrary {
  fn from_int(Int) -> Self
  fn signum(self : Self) -> Int
  fn abs(self : Self) -> Self
  fn to_bigint(self : Self) -> BigInt
  fn from_bigint(BigInt) -> Self?
  fn compare_fraction(Self, Self, Self, Self) -> Int
}

///|
pub impl Integral for Int64 with fn from_int(i : Int) -> Int64 {
  i.to_int64()
}

///|
pub impl Integral for Int64 with fn abs(self : Int64) -> Int64 {
  self.abs()
}

///|
pub impl Integral for Int64 with fn signum(self : Int64) -> Int {
  self.compare(0L)
}

///|
pub impl Integral for Int64 with fn to_bigint(self : Int64) -> BigInt {
  BigInt::from_int64(self)
}

///|
pub impl Integral for Int64 with fn from_bigint(b : BigInt) -> Int64? {
  if b.compare_int64(@int64.MIN_VALUE) >= 0 &&
    b.compare_int64(@int64.MAX_VALUE) <= 0 {
    Some(b.to_int64())
  } else {
    None
  }
}

///|
pub impl Integral for Int with fn from_int(i : Int) -> Int {
  i
}

///|
pub impl Integral for Int with fn abs(self : Int) -> Int {
  self.abs()
}

///|
pub impl Integral for Int with fn signum(self : Int) -> Int {
  self.compare(0)
}

///|
pub impl Integral for Int with fn to_bigint(self : Int) -> BigInt {
  BigInt::from_int(self)
}

///|
pub impl Integral for Int with fn from_bigint(b : BigInt) -> Int? {
  if b.compare_int(@int.MIN_VALUE) >= 0 && b.compare_int(@int.MAX_VALUE) <= 0 {
    Some(b.to_int())
  } else {
    None
  }
}

///|
pub impl Integral for BigInt with fn from_int(i : Int) -> BigInt {
  BigInt::from_int(i)
}

///|
pub impl Integral for BigInt with fn abs(self : BigInt) -> BigInt {
  if self < 0N {
    -self
  } else {
    self
  }
}

///|
pub impl Integral for BigInt with fn signum(self : BigInt) -> Int {
  self.compare(0N)
}

///|
pub impl Integral for BigInt with fn to_bigint(self : BigInt) -> BigInt {
  self
}

///|
pub impl Integral for BigInt with fn from_bigint(b : BigInt) -> BigInt? {
  Some(b)
}

///|
pub type Rational64 = Rational[Int64]

///|
pub type Rational32 = Rational[Int]

///|
pub type BigRational = Rational[BigInt]

///|
#deprecated
pub extend Rational with Eq::{not_equal, equal}