// Copyright (c) 2023 IBM Corp. // All rights reserved. // // 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. package ord import ( C "github.com/IBM/fp-go/constraints" E "github.com/IBM/fp-go/eq" F "github.com/IBM/fp-go/function" P "github.com/IBM/fp-go/predicate" ) type Ord[T any] interface { E.Eq[T] Compare(x, y T) int } type ord[T any] struct { c func(x, y T) int e func(x, y T) bool } func (self ord[T]) Equals(x, y T) bool { return self.e(x, y) } func (self ord[T]) Compare(x, y T) int { return self.c(x, y) } // ToEq converts an [Ord] to [E.Eq] func ToEq[T any](o Ord[T]) E.Eq[T] { return o } // MakeOrd creates an instance of an Ord func MakeOrd[T any](c func(x, y T) int, e func(x, y T) bool) Ord[T] { return ord[T]{c: c, e: e} } // MakeOrd creates an instance of an Ord from a compare function func FromCompare[T any](compare func(T, T) int) Ord[T] { return MakeOrd(compare, func(x, y T) bool { return compare(x, y) == 0 }) } // Reverse creates an inverted ordering func Reverse[T any](o Ord[T]) Ord[T] { return MakeOrd(func(y, x T) int { return o.Compare(x, y) }, o.Equals) } // Contramap creates an odering under a transformation function func Contramap[A, B any](f func(B) A) func(Ord[A]) Ord[B] { return func(o Ord[A]) Ord[B] { return MakeOrd(func(x, y B) int { return o.Compare(f(x), f(y)) }, func(x, y B) bool { return o.Equals(f(x), f(y)) }) } } // Min takes the minimum of two values. If they are considered equal, the first argument is chosen func Min[A any](o Ord[A]) func(A, A) A { return func(a, b A) A { if o.Compare(a, b) < 1 { return a } return b } } // Max takes the maximum of two values. If they are considered equal, the first argument is chosen func Max[A any](o Ord[A]) func(A, A) A { return func(a, b A) A { if o.Compare(a, b) >= 0 { return a } return b } } // Clamp clamps a value between a minimum and a maximum func Clamp[A any](o Ord[A]) func(A, A) func(A) A { return func(low, hi A) func(A) A { clow := F.Bind2nd(o.Compare, low) chi := F.Bind2nd(o.Compare, hi) return func(a A) A { if clow(a) <= 0 { return low } if chi(a) >= 0 { return hi } return a } } } func strictCompare[A C.Ordered](a, b A) int { if a < b { return -1 } else if a > b { return +1 } else { return 0 } } func strictEq[A comparable](a, b A) bool { return a == b } // FromStrictCompare implements the ordering based on the built in native order func FromStrictCompare[A C.Ordered]() Ord[A] { return MakeOrd(strictCompare[A], strictEq[A]) } // Lt tests whether one value is strictly less than another func Lt[A any](O Ord[A]) func(A) func(A) bool { return func(second A) func(A) bool { return func(first A) bool { return O.Compare(first, second) < 0 } } } // Leq Tests whether one value is less or equal than another func Leq[A any](O Ord[A]) func(A) func(A) bool { return func(second A) func(A) bool { return func(first A) bool { return O.Compare(first, second) <= 0 } } } /** * Test whether one value is strictly greater than another */ func Gt[A any](O Ord[A]) func(A) func(A) bool { return func(second A) func(A) bool { return func(first A) bool { return O.Compare(first, second) > 0 } } } // Geq tests whether one value is greater or equal than another func Geq[A any](O Ord[A]) func(A) func(A) bool { return func(second A) func(A) bool { return func(first A) bool { return O.Compare(first, second) >= 0 } } } // Between tests whether a value is between a minimum (inclusive) and a maximum (exclusive) func Between[A any](O Ord[A]) func(A, A) func(A) bool { lt := Lt(O) geq := Geq(O) return func(lo, hi A) func(A) bool { // returns the predicate return P.And(lt(hi))(geq(lo)) } }