Task: Define a primitive data type



C#: using System;
using System.Globalization;

struct LimitedInt : IComparable, IComparable<LimitedInt>, IConvertible, IEquatable<LimitedInt>, IFormattable
{
    const int MIN_VALUE = 1;
    const int MAX_VALUE = 10;

    public static readonly LimitedInt MinValue = new LimitedInt(MIN_VALUE);
    public static readonly LimitedInt MaxValue = new LimitedInt(MAX_VALUE);

    static bool IsValidValue(int value) => value >= MIN_VALUE && value <= MAX_VALUE;

    readonly int _value;
    public int Value => this._value == 0 ? MIN_VALUE : this._value; 

    public LimitedInt(int value)
    {
        if (!IsValidValue(value))
            throw new ArgumentOutOfRangeException(nameof(value), value, $"Value must be between {MIN_VALUE} and {MAX_VALUE}.");
        this._value = value;
    }

    #region IComparable
    public int CompareTo(object obj)
    {
        if (obj is LimitedInt l) return this.Value.CompareTo(l);
        throw new ArgumentException("Object must be of type " + nameof(LimitedInt), nameof(obj));
    }
    #endregion

    #region IComparable<LimitedInt>
    public int CompareTo(LimitedInt other) => this.Value.CompareTo(other.Value);
    #endregion

    #region IConvertible
    public TypeCode GetTypeCode() => this.Value.GetTypeCode();
    bool IConvertible.ToBoolean(IFormatProvider provider) => ((IConvertible)this.Value).ToBoolean(provider);
    byte IConvertible.ToByte(IFormatProvider provider) => ((IConvertible)this.Value).ToByte(provider);
    char IConvertible.ToChar(IFormatProvider provider) => ((IConvertible)this.Value).ToChar(provider);
    DateTime IConvertible.ToDateTime(IFormatProvider provider) => ((IConvertible)this.Value).ToDateTime(provider);
    decimal IConvertible.ToDecimal(IFormatProvider provider) => ((IConvertible)this.Value).ToDecimal(provider);
    double IConvertible.ToDouble(IFormatProvider provider) => ((IConvertible)this.Value).ToDouble(provider);
    short IConvertible.ToInt16(IFormatProvider provider) => ((IConvertible)this.Value).ToInt16(provider);
    int IConvertible.ToInt32(IFormatProvider provider) => ((IConvertible)this.Value).ToInt32(provider);
    long IConvertible.ToInt64(IFormatProvider provider) => ((IConvertible)this.Value).ToInt64(provider);
    sbyte IConvertible.ToSByte(IFormatProvider provider) => ((IConvertible)this.Value).ToSByte(provider);
    float IConvertible.ToSingle(IFormatProvider provider) => ((IConvertible)this.Value).ToSingle(provider);
    string IConvertible.ToString(IFormatProvider provider) => this.Value.ToString(provider);
    object IConvertible.ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)this.Value).ToType(conversionType, provider);
    ushort IConvertible.ToUInt16(IFormatProvider provider) => ((IConvertible)this.Value).ToUInt16(provider);
    uint IConvertible.ToUInt32(IFormatProvider provider) => ((IConvertible)this.Value).ToUInt32(provider);
    ulong IConvertible.ToUInt64(IFormatProvider provider) => ((IConvertible)this.Value).ToUInt64(provider);
    #endregion

    #region IEquatable<LimitedInt>
    public bool Equals(LimitedInt other) => this == other;
    #endregion

    #region IFormattable
    public string ToString(string format, IFormatProvider formatProvider) => this.Value.ToString(format, formatProvider);
    #endregion

    #region operators
    public static bool operator ==(LimitedInt left, LimitedInt right) => left.Value == right.Value;
    public static bool operator !=(LimitedInt left, LimitedInt right) => left.Value != right.Value;
    public static bool operator <(LimitedInt left, LimitedInt right) => left.Value < right.Value;
    public static bool operator >(LimitedInt left, LimitedInt right) => left.Value > right.Value;
    public static bool operator <=(LimitedInt left, LimitedInt right) => left.Value <= right.Value;
    public static bool operator >=(LimitedInt left, LimitedInt right) => left.Value >= right.Value;

    public static LimitedInt operator ++(LimitedInt left) => (LimitedInt)(left.Value + 1);
    public static LimitedInt operator --(LimitedInt left) => (LimitedInt)(left.Value - 1);

    public static LimitedInt operator +(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value + right.Value);
    public static LimitedInt operator -(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value - right.Value);
    public static LimitedInt operator *(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value * right.Value);
    public static LimitedInt operator /(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value / right.Value);
    public static LimitedInt operator %(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value % right.Value);

    public static LimitedInt operator &(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value & right.Value);
    public static LimitedInt operator |(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value | right.Value);
    public static LimitedInt operator ^(LimitedInt left, LimitedInt right) => (LimitedInt)(left.Value ^ right.Value);
    public static LimitedInt operator ~(LimitedInt left) => (LimitedInt)~left.Value;

    public static LimitedInt operator >>(LimitedInt left, int right) => (LimitedInt)(left.Value >> right);
    public static LimitedInt operator <<(LimitedInt left, int right) => (LimitedInt)(left.Value << right);

    public static implicit operator int(LimitedInt value) => value.Value;
    public static explicit operator LimitedInt(int value)
    {
        if (!IsValidValue(value)) throw new OverflowException();
        return new LimitedInt(value);
    }
    #endregion

    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null)
        => this.Value.TryFormat(destination, out charsWritten, format, provider);

    public override int GetHashCode() => this.Value.GetHashCode();
    public override bool Equals(object obj) => obj is LimitedInt l && this.Equals(l);
    public override string ToString() => this.Value.ToString();

    #region static methods
    public static bool TryParse(ReadOnlySpan<char> s, out int result) => int.TryParse(s, out result);
    public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out int result) => int.TryParse(s, style, provider, out result);
    public static int Parse(string s, IFormatProvider provider) => int.Parse(s, provider);
    public static int Parse(string s, NumberStyles style, IFormatProvider provider) => int.Parse(s, style, provider);
    public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, ref int result) => int.TryParse(s, style, provider, out result);
    public static int Parse(string s) => int.Parse(s);
    public static int Parse(string s, NumberStyles style) => int.Parse(s, style);
    public static int Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) => int.Parse(s, style, provider);
    public static bool TryParse(string s, ref int result) => int.TryParse(s, out result);
    #endregion
}








Fortran: module Bounded
  implicit none

  type BoundedInteger
     integer, private :: v         
     integer, private :: from, to  
     logical, private :: critical
  end type BoundedInteger

  interface assignment(=)
     module procedure bounded_assign_bb, bounded_assign_bi 
                    
  end interface

  interface operator(+)
     module procedure bounded_add_bbb 
                    
                    
                    
  end interface

  private :: bounded_assign_bb, bounded_assign_bi, &
             bounded_add_bbb

contains

  subroutine set_bound(bi, lower, upper, critical, value)
    type(BoundedInteger), intent(out) :: bi
    integer, intent(in) :: lower, upper
    integer, intent(in), optional :: value
    logical, intent(in), optional :: critical

    bi%from = min(lower, upper)
    bi%to = max(lower, upper)
    if ( present(critical) ) then
       bi%critical = critical
    else
       bi%critical = .false.
    end if
    if ( present(value) ) then
       bi = value
    end if
  end subroutine set_bound

  subroutine bounded_assign_bb(a, b)
    type(BoundedInteger), intent(out) :: a
    type(BoundedInteger), intent(in)  :: b

    call bounded_assign_bi(a, b%v)

  end subroutine bounded_assign_bb


  subroutine bounded_assign_bi(a, b)
    type(BoundedInteger), intent(out) :: a
    integer,              intent(in)  :: b

    if ( (a%from <= b) .and. (a%to >= b) ) then
       a%v = b
    else
       write(0,*) "BoundedInteger: out of bound assignment"
       if ( a%critical ) then
          stop 
       else
          if ( b < a%from ) then
             a%v = a%from
          else
             a%v = a%to
          end if
          write(0,"(A,' (',I0, ')')") "BoundedInteger: set to nearest bound", a%v
       end if
    end if
  end subroutine bounded_assign_bi


  function bounded_add_bbb(a, b) result(c)
    type(BoundedInteger) :: c
    type(BoundedInteger), intent(in) :: a, b

    integer :: t

    c%from = max(a%from, b%from)
    c%to   = min(a%to,   b%to)
    t = a%v + b%v
    if ( c%from <= t .and. c%to >= t ) then
       c%v = t
    else
       write(0,*) "BoundedInteger: out of bound sum"
       if ( a%critical .or. b%critical ) then
          stop
       else
          if ( t < c%from ) then
             c%v = c%from
          else
             c%v = c%to
          end if
          write(0,"(A,' (',I0,')')") "BoundedInteger: set to nearest bound", c%v
       end if
    end if
  end function bounded_add_bbb

end module Bounded